Bug fix ledger
This page lists every bug fixed since PiG 0.4.1: what users saw, why it happened, what it cost them, where it happened, which commits fixed it, and which tests keep it fixed. make bug-ledger regenerates it and make bug-ledger-check fails when it is out of date.
How to read it
- Source. Each entry has one source class. Own defect: PiG differed from Pi, or failed where Pi does not. Fixed ahead of Pi: Pi has the same defect, PiG fixed it first, and the entry names its divergence number (listed by
pig docs show divergences), the Pi issue, and the condition that removes the difference when Pi fixes it. Pi fix ported: Pi shipped the fix and PiG ported it. - What counts as a bug. An entry exists for each item under Fixed in the release notes, for each fixed-ahead divergence added since PiG 0.4.1, and for each commit titled as a fix that changes shipped code. Commits titled as fixes that change only tests, tooling, generated files or the repair of a merge are not bugs in a release; they are counted at the end of this page.
- Root cause. The text comes from the fix commit's message or the release note, in that order. Not recorded means neither states a cause.
- User impact. A class chosen from the wording of the release note: credential exposure, crash or hang, lost data, a failed operation, wrong display, wrong output, or a difference from Pi. It describes the report, not a measured severity.
- Fix commits. Short hashes from the project's development history, with the pull request number when the commit has one. Not yet committed marks a fix whose release note is written but whose commit is not in history.
- Tests. Tests the fix commit adds or names, each checked to exist in the tree. None recorded marks a fix whose commit neither adds nor names one.
Summary
Entries are listed by ID. The page carries no totals, so a pull request that adds an entry changes only that entry's lines; go run ./automation/ci/bugledger -summary prints the counts by source and the number of entries with no recorded test.
ID | Bug | Source | Platform | Fixed in |
| BF-003f6f0 | Ctrl+U, Ctrl+K and the word-delete keys leave an open autocomplete list open, as Pi's editor does | PiG's own defect | All platforms | 0.5.0 |
| BF-007c91d | Go, Python and Rust extensions: in a before_agent_start handler, the event's systemPrompt and the… | PiG's own defect | All platforms | 0.5.0 |
| BF-02114ff | fix(tui): filterable list drops the label when no cell remains (Pi select-list) | PiG's own defect | All platforms | Unreleased |
| BF-0225a43 | A device-attributes reply delivered in slow reads no longer reaches the editor as input: an ESC[? prefix… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-0225bbf | Pressing Escape twice with only whitespace in the editor now opens the tree or fork selector, as in Pi, which… | PiG's own defect | All platforms | 0.5.0 |
| BF-022d99f | fix(tui): the login dialog's input renders at every prompt position | PiG's own defect | All platforms | Unreleased |
| BF-037b2e3 | A session message whose members are in another order than Pi writes them, or that holds members PiG does not… | PiG's own defect | All platforms | 0.5.0 |
| BF-03ad544 | fix(tui): card of an unregistered tool is Pi's formatToolExecution | PiG's own defect | All platforms | Unreleased |
| BF-040088e | ai.CloudflareStreams drops the deferred fetch and cancel operations of the API it wraps, as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-04ef0ac | Closing a Chord host no longer hangs when a hold arrives over a link that closes at the same time | PiG's own defect | Windows | Unreleased |
| BF-051a79d | A Provider object registered by an extension (pi.registerProvider(provider)) now receives the session's… | PiG's own defect | All platforms | 0.5.0 |
| BF-051a79d-2 | A Provider object's stream options no longer carry a stray hasOnPayload value | PiG's own defect | All platforms | 0.5.0 |
| BF-053d96d | RPC mode: when an extension command calls pi.sendUserMessage() or `pi.sendMessage(..., { triggerTurn: true… | PiG's own defect | All platforms | 0.5.0 |
| BF-056bbfb | Key matching follows Pi for three terminal inputs | PiG's own defect | All platforms | 0.5.0 |
| BF-058e7a0 | The Go, Rust and Python SDKs export Pi's compaction helpers calculateContextTokens, shouldCompact… | PiG's own defect | All platforms | 0.5.0 |
| BF-0630078 | MCP tool calls no longer fail with "Invalid MCP tools/call structured content" when the server returns… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-0631c73 | Go, Python and Rust extensions now draw an overlay component (ctx.custom with overlay: true) at the… | PiG's own defect | All platforms | 0.5.0 |
| BF-064181f | fix(extension): hold the theme colors map the wire palette memo is keyed on | PiG's own defect | All platforms | Unreleased |
| BF-07151d0 | In print and JSON mode, Node extensions in separate processes now keep Pi's order when a later extension's… | PiG's own defect | All platforms | 0.5.0 |
| BF-0790d5e | String.prototype.trim vs strings.TrimSpace: four Pi .trim() sites used Go's strings.TrimSpace, which… | PiG's own defect | All platforms | 0.5.0 |
| BF-081e8c3 | Bug report files against live Pi: new TestBugReportFilesMatchPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-082355f | The session picker checks its keys in Pi's order when bindings overlap (delete before rename, list actions… | PiG's own defect | All platforms | 0.5.0 |
| BF-082f350 | markdown.codeBlockIndent: Pi's getter is markdown?.codeBlockIndent ?? " ", so an authored "" means no… | PiG's own defect | All platforms | 0.5.0 |
| BF-086395a | coding.DefaultAgentDir() (Pi's getAgentDir) now returns the directory named by PIG_CODING_AGENT_DIR… | PiG's own defect | All platforms | 0.5.0 |
| BF-08bf076 | An extension login callback (onPrompt, onSelect, onManualCodeInput) whose prompt fails for a reason… | PiG's own defect | All platforms | 0.5.0 |
| BF-093cf5c | Provider error text is written the way Pi writes it | PiG's own defect | All platforms | 0.5.0 |
| BF-098e33d | Extension ctx.ui.theme.fg, bg and getBgAnsi throw Unknown theme color: <token> in every SDK for a… | PiG's own defect | All platforms | 0.5.0 |
| BF-0a3e8ce | In print and JSON mode, SIGTERM or SIGHUP no longer hangs when a Go, Python or Rust extension's… | PiG's own defect | All platforms | 0.5.0 |
| BF-0aa31c4 | Extensions that import @earendil-works/pi-ai/models (for example for clampThinkingLevel) load in the Node… | A fix Pi shipped that PiG ported | All platforms | 0.5.2 |
| BF-0aba908 | Extensions' ctx.ui.setHiddenThinkingLabel() now changes the collapsed thinking label of every assistant… | PiG's own defect | All platforms | 0.5.0 |
| BF-0adc7a6 | A provider that an extension registers while it loads no longer fails the load when the model registry… | PiG's own defect | All platforms | Unreleased |
| BF-0ae3fcf | The login provider list selects with Enter when tui.select.confirm is rebound, as Pi's search input submits… | PiG's own defect | All platforms | 0.5.0 |
| BF-0ae4683 | PiG, the extension SDKs and the sprite selection use one configuration root: PIG_HOME, else… | PiG's own defect | All platforms | 0.5.0 |
| BF-0b13d8b | pig auth --help names pig auth ... instead of pi auth ... | PiG's own defect | All platforms | 0.5.0 |
| BF-0b13d8b-2 | pig extension --help lists pig extension init --login | PiG's own defect | All platforms | 0.5.0 |
| BF-0b13d8b-3 | pig --help lists pig login, pig logout, pig package, pig extension upgrade, pig reload, and `pig… | PiG's own defect | All platforms | 0.5.0 |
| BF-0b4e1e3 | The OpenAI Codex base URL keeps the whitespace around it, as Pi does (only an all-blank value falls back to… | PiG's own defect | All platforms | 0.5.0 |
| BF-0b674b3 | Gemini model-id checks follow Pi's lowercasing: an id spelled with a dotted capital İ or a long ſ is no… | PiG's own defect | All platforms | 0.5.0 |
| BF-0b85f55 | The Rust SDK keeps every session entry when one entry of the session file holds an unpaired surrogate (it… | PiG's own defect | All platforms | 0.5.0 |
| BF-0bbe365 | fix(codingagent): footer context usage refreshes on a context_edit entry (Pi getContextUsage) | PiG's own defect | All platforms | Unreleased |
| BF-0bc05bf | Fixed ! and RPC bash output keeping fragments of color codes, such as a stray m, when a code was split… | PiG's own defect | All platforms | 0.5.0 |
| BF-0ce459e | /login offers a provider object's own login methods (Pi's registerNativeProvider, the… | PiG's own defect | All platforms | 0.5.0 |
| BF-0d0a3ef | A turn through OpenRouter (Chat Completions or Anthropic Messages) now reports the cost OpenRouter billed… | PiG's own defect | All platforms | 0.5.0 |
| BF-0d13b01 | SettingsManager.getTransport and fifteen sibling getters (settings-manager.ts: getSteeringMode… | PiG's own defect | All platforms | 0.5.0 |
| BF-0d1c973 | tui.Box no longer invalidates its children when a child is added or removed or the box is cleared | PiG's own defect | All platforms | 0.5.0 |
| BF-0d55b30 | A panicking notify while MCP reports startup problems is shown as "MCP failed to load" instead of ending the… | PiG's own defect | All platforms | 0.5.0 |
| BF-0d55b30-2 | The Python SDK reports a component whose set_invalidate(None) raises during cleanup in the extension log… | PiG's own defect | All platforms | 0.5.0 |
| BF-0dff068 | An OpenAI Responses function call streamed with an empty call_id no longer makes every later request fail… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-0e05901 | A provider an extension registers with refreshModels and no streamSimple streams through its API, as in Pi | PiG's own defect | All platforms | 0.5.0 |
| BF-0e225b4 | fix(runtimecell): keep the Python runner's _run callable without the built event | PiG's own defect | All platforms | Unreleased |
| BF-0e3411c | Fixed CI on main after the 0.5.1 ledger trim: the reviewed-pin ledgers (upstream-v0.84.0.json… | PiG's own defect | All platforms | 0.5.2 |
| BF-0e360a0 | The tui package's default keybindings no longer depend on the platform: undo is ctrl+- and the alt-screen… | PiG's own defect | Windows, WSL | 0.5.0 |
| BF-0e8761b | A system message with an empty sections object is stored as "sections": {}, as Pi stores it after a reset | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-0ffb06a | fix(tui): diff rows with a CR are wrapped like Pi's Text | PiG's own defect | All platforms | Unreleased |
| BF-116b36c | fix(extensions): answer the start-window report of a tool call that holds no window, where the report is read | PiG's own defect | All platforms | Unreleased |
| BF-11a6fcb | fix(extension): a restart adopts the new renderers; the late-registration row runs what it registered | PiG's own defect | All platforms | Unreleased |
| BF-11b199d | Session lines (and every other JSON.stringify-mode encode) now write ill-formed UTF-8 the way Pi does: one… | PiG's own defect | All platforms | 0.5.0 |
| BF-1268115 | fix(model runtime): a cancelled request to a caller-owned provider the runtime does not know is aborted, not… | PiG's own defect | All platforms | Unreleased |
| BF-1356aae | fix(train): load pig_sdk.tui with the package, run the TS queue check with no runtime, pin three fixture… | PiG's own defect | All platforms | Unreleased |
| BF-1364791 | Stopping a codemode script or reaching its deadline no longer waits up to half a second for a built-in that… | PiG's own defect | All platforms | 0.5.0 |
| BF-1390c9d | fix(ai): the Anthropic event reader matches the type member exactly | PiG's own defect | All platforms | Unreleased |
| BF-155d0f3 | In RPC mode, SIGTERM or SIGHUP during a tool call likewise stops the run while the session_shutdown… | PiG's own defect | All platforms | 0.5.0 |
| BF-15a5c0b | The Kimi Code, Meta and xAI device logins judge the verification URI the way Pi does, and Meta and xAI open… | PiG's own defect | All platforms | 0.5.0 |
| BF-160d95a | In RPC mode, a run that an extension command starts with sendMessage or sendUserMessage now emits… | PiG's own defect | All platforms | 0.5.0 |
| BF-160d95a-2 | In RPC mode, the Go, Python and Rust SDKs no longer let the host move on while a notify, setStatus… | PiG's own defect | All platforms | 0.5.0 |
| BF-1631753 | Queueing a steering or follow-up message while the agent streams prints no status line, as Pi does | PiG's own defect | All platforms | 0.5.0 |
| BF-163f7b7 | Empty quoted prompt-template arguments ("" or '') are kept as empty arguments, so later positional… | PiG's own defect | All platforms | 0.5.0 |
| BF-165cf58 | pig package publish --to npm and pig piglet publish --to npm no longer run an npm that the published… | PiG's own defect | Windows | 0.5.0 |
| BF-165cf58-2 | On Windows, publishing to npm now works when npm is an npm.cmd at a path with spaces, such as `C:\Program… | PiG's own defect | Windows | 0.5.0 |
| BF-167dae3 | fix(codingagent): the idle collection runs two GC cycles again so pool victims are freed (D172) | PiG's own defect | All platforms | Unreleased |
| BF-16f8d39 | fix(codingagent): keep the clock allowance on the marker header's time.Now line | PiG's own defect | All platforms | Unreleased |
| BF-1827831 | The MCP extension's tool_call handler passes a tool's reachesTools the call's input in the member order… | PiG's own defect | All platforms | 0.5.0 |
| BF-1848633 | Extension widgets share one key namespace across extensions, as in Pi: an extension that sets a widget key… | PiG's own defect | All platforms | 0.5.0 |
| BF-184ed40 | fix(hljs): a language's reach does not depend on the first text it highlights; red cases for resumeLimit | PiG's own defect | All platforms | Unreleased |
| BF-1896331 | Changing the thinking level in interactive mode (cycle key, /thinking, the settings selector) goes through… | PiG's own defect | All platforms | 0.5.0 |
| BF-18daf7f | The Go SDK's typed branch entry keeps the text, thinking and tool calls of an assistant message whose tool… | PiG's own defect | All platforms | 0.5.0 |
| BF-18f7d39 | Codemode @options errors report V8's JSON error text, tool and schema declarations keep unpaired… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-1971aae | A bash shell that starts at the moment pig handles an emergency exit, a crash or a signal is now killed… | PiG's own defect | All platforms | 0.5.0 |
| BF-19a07f0 | fix(sdk-rs): give the request-start wire test's RequestMsg the batch, child_session, tool_call_index and… | PiG's own defect | All platforms | Unreleased |
| BF-1a855cd | The /session block now follows a later theme change and shows the cache-warming countdown as of each… | PiG's own defect | All platforms | 0.5.0 |
| BF-1b5540a | client.Client.Disconnect takes the reason string as Pi's Client.disconnect(reason) does (it was an… | PiG's own defect | All platforms | 0.5.0 |
| BF-1c6b864 | A Session that an extension creates with createAgentSession, or that coding.CreateAgentSession returns… | PiG's own defect | All platforms | Unreleased |
| BF-1ca0e2f | fix(codingagent): the terminal reader yields to the main loop it readied before it polls again | PiG's own defect | All platforms | Unreleased |
| BF-1cc7519 | fix(sdk): a command, shortcut, flag or renderer registered after load reaches the host in every SDK | PiG's own defect | All platforms | Unreleased |
| BF-1ce00b0 | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-1d02c73 | Filters that Pi writes with toLowerCase() (select lists, the config selector, file autocomplete, /model… | PiG's own defect | All platforms | 0.5.0 |
| BF-1d1c8aa | RPC mode now ends by the signal when a termination signal arrives while a quit session_shutdown handler is… | PiG's own defect | All platforms | 0.5.0 |
| BF-1d5b272 | A text file passed as an @file argument that holds invalid UTF-8 now reaches the prompt as Pi sends it… | PiG's own defect | All platforms | 0.5.0 |
| BF-1dcaef7 | Durable: a JSONL line that starts with a byte order mark now opens, as in Pi, instead of failing with… | PiG's own defect | All platforms | 0.5.0 |
| BF-1ed43b3 | fix(train): load pig_sdk.tui with the package, run the TS queue check with no runtime, pin three fixture… | PiG's own defect | All platforms | Unreleased |
| BF-1ee7dea | The Mistral request URL for a base URL with a non-special scheme or a host or port the WHATWG parser rejects… | PiG's own defect | All platforms | 0.5.0 |
| BF-1f7fa2f | In print and JSON mode, an extension in its own Node process no longer runs its callbacks with a stale ctx… | PiG's own defect | All platforms | 0.5.0 |
| BF-1fbdabb | A CRLF that a proxy splits across two network chunks no longer aborts an Anthropic turn with `Could not parse… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-1fcf017 | toUpperCase ports use a shared JavaScript jsstring.ToUpper (matches Node over every code point).… | PiG's own defect | All platforms | 0.5.0 |
| BF-202b8f1 | On arm64 and other CPUs with a fused multiply-add, the OKHSL saturation of a color (used when generating the… | PiG's own defect | All platforms | 0.5.0 |
| BF-205933e | fix(ai): a Gemini model outside the catalog carries the provider's API, so replay's isSameModel matches Pi… | PiG's own defect | All platforms | Unreleased |
| BF-20718f7 | fix(coding-agent): resolve model references through one provider selection policy (Pi) | PiG's own defect | All platforms | Unreleased |
| BF-20aee29 | The crash notice prints the year of a crash time the way Node's toLocaleString does: unpadded, and counted… | PiG's own defect | All platforms | 0.5.0 |
| BF-211af49 | /hotkeys lists extension shortcuts, and the extension shortcut listener tries them, in the order of Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-2165463 | On Windows, saving credentials to auth.json, models.json or the MCP OAuth store no longer fails at once… | PiG's own defect | Windows | 0.5.0 |
| BF-224dbd9 | On Linux and macOS, the bash tool, a ! command and an extension's exec no longer report no output for a… | PiG's own defect | macOS, Linux | Unreleased |
| BF-2280bf8 | The per-API stream entry points (ai.AnthropicMessagesAPI() through ai.PiMessagesAPI() and the deprecated… | PiG's own defect | All platforms | 0.5.0 |
| BF-2309604 | Tool schemas in OpenAI Completions, OpenAI Responses, Codex and Mistral request bodies keep the members in… | PiG's own defect | All platforms | 0.5.0 |
| BF-2316fe3 | The Piglet JSON Schema that pig piglet schema prints rejected powershell in tools, which `pig piglet… | PiG's own defect | Windows | 0.5.3 |
| BF-234316d | On Windows, a Go extension's source hash no longer fails, or makes a build beside it fail, with `The process… | PiG's own defect | Windows | Unreleased |
| BF-24872ab | Extensions receive each model (ctx.model, model_select, ctx.modelRegistry reads, scoped models… | PiG's own defect | All platforms | 0.5.0 |
| BF-24a821d | The openai stream decoder follows openai 7.19.0, the version packages/ai declares: the stream ends at [DONE]… | PiG's own defect | All platforms | 0.5.0 |
| BF-24a9b69 | fix(hljs): a resumed run counts the scan iterations before its checkpoint | PiG's own defect | All platforms | Unreleased |
| BF-25aa7fd | A subprocess extension's markdown transform that was busy past the five-second renderer inactivity boundary… | PiG's own defect | All platforms | 0.5.0 |
| BF-25c854d | Session directories (the agent directory, sessions and each project's directory) are created owner-only… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-2699291 | The serializeConversation parity row records that Rust session entries still read an unpaired surrogate as… | PiG's own defect | All platforms | 0.5.0 |
| BF-26b6705 | The RPC test harness no longer races on its recorded output error (found by a go test -race ./coding/...… | PiG's own defect | All platforms | 0.5.0 |
| BF-26d302a | A login flow's select prompt opens a selector in the login dialog, as Pi's does, instead of ending the… | PiG's own defect | All platforms | 0.5.0 |
| BF-26d302a-2 | A provider object's own auth.apiKey.login runs in the login dialog, as Pi's showApiKeyLoginDialog runs… | PiG's own defect | All platforms | 0.5.0 |
| BF-26d302a-3 | A provider object's login that sends a prompt or event type Pi does not define fails with an error that names… | PiG's own defect | All platforms | 0.5.0 |
| BF-27a1650 | The keybindings manager's key text and key matching now cover every keybinding the merged table defines… | PiG's own defect | All platforms | 0.5.0 |
| BF-27ddc30 | A tool card shows the arguments of a tool call while they stream, as Pi does: a streamed write card shows its… | PiG's own defect | All platforms | 0.5.0 |
| BF-27f3324 | The npm release packer reads npm pack --json output from npm 12, which keys the result by package name, as… | PiG's own defect | All platforms | 0.5.0 |
| BF-28b8425 | Blank-key detection for compat API keys, the Vertex API key and the Vertex custom base URL trim whitespace… | PiG's own defect | All platforms | 0.5.0 |
| BF-28f8b3b | fix(tui): editor wrap of a one-grapheme segment wider than the width no longer overflows the stack (D158) | PiG's own defect | All platforms | Unreleased |
| BF-2a055a7 | An extension dialog (select, confirm, input, editor) opened while a focused ctx.ui.custom component… | PiG's own defect | All platforms | 0.5.0 |
| BF-2a0fb96 | /cost lists its tools in a stable order: formatCostSummary ranged over snap.ToolTotals, so the Tools:… | PiG's own defect | All platforms | 0.5.0 |
| BF-2a919a4 | An indented continuation line of a list item loses up to four spaces, a lazy line after a quoted list item… | PiG's own defect | All platforms | 0.5.0 |
| BF-2ab79ce | An extension provider whose streamSimple hands its request to pi-ai's compat streamSimple (or stream)… | PiG's own defect | All platforms | 0.5.0 |
| BF-2ab79ce-2 | A provider registered with its own streamSimple runs that callback only for its models whose api is the… | PiG's own defect | All platforms | 0.5.0 |
| BF-2ad7249 | fix(ledger): close the getJsonSchemaToolParameters and google convertTools rows | PiG's own defect | All platforms | Unreleased |
| BF-2be0883 | fix(extensions): parallel Node runtime linking reports the walk-order first failure under every schedule | PiG's own defect | All platforms | Unreleased |
| BF-2c1959c | A Mistral stream is no longer aborted mid-generation by the request timeout: the timeout now covers only the… | PiG's own defect | All platforms | 0.5.0 |
| BF-2c5db8b | A Piglet Binary with a fused Go extension starts within a few milliseconds of Stock PiG | PiG's own defect | All platforms | 0.5.0 |
| BF-2c88c5f | Ill-formed UTF-8 in a string that PiG re-encodes as JSON (an extension's raw JSON, tool schemas, code-mode… | PiG's own defect | All platforms | 0.5.0 |
| BF-2d05dc8 | A models.json cost that sets tiers: [] (a custom model's cost, or a modelOverrides cost) keeps… | PiG's own defect | All platforms | 0.5.0 |
| BF-2d71556 | fix(codingagent): queued-message lines use the dim foreground; hint separator closes only the foreground | PiG's own defect | All platforms | Unreleased |
| BF-2d99958 | A terminal's reply to the startup color query now applies the colors and ends the startup wait before the… | PiG's own defect | All platforms | 0.5.0 |
| BF-2ea8ad2 | The openai-completions, openai-responses and openrouter-images requests build their URL as the openai SDK… | PiG's own defect | All platforms | 0.5.0 |
| BF-2f881ba | "Your first extension" now says that pig install --validate-only exits 1 when the extension does not build | PiG's own defect | All platforms | 0.5.0 |
| BF-2fc2307 | A scalar that names a collection tag (!!map 1, !!seq true) inside a complex mapping key stays plain in… | PiG's own defect | All platforms | 0.5.0 |
| BF-305bd8b | A durable task that panics with a value whose String method panics now faults with the message `[object… | PiG's own defect | All platforms | 0.5.0 |
| BF-30d4a6d | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-30e47f4 | fix(tui): SetThemeInstance notifies the active theme observers, as Pi's setThemeInstance runs its callback | PiG's own defect | All platforms | Unreleased |
| BF-3154dfb | Starting a Go extension no longer fails with `hash <dir> after its build: open …/release/deps/rmeta…: no… | PiG's own defect | All platforms | Unreleased |
| BF-31740ec | A waitForIdle() waiter registered during a run stays pending through agent_settled handlers and the… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-31a7d77 | SessionBeforeTreeResultSummary.Usage is typed as ai.Usage | PiG's own defect | All platforms | 0.5.0 |
| BF-31cde9f | A Node extension that calls pi.appendEntry now sees the new entry in ctx.sessionManager.getEntries() in… | PiG's own defect | All platforms | 0.5.0 |
| BF-31d09fa | Fixed OpenRouter router models such as openrouter/auto showing a cost of -$1,000,000 per 1M tokens… | PiG's own defect | All platforms | 0.5.0 |
| BF-31d85ab | fix duplicate jsstring import in tui/text.go | PiG's own defect | All platforms | Unreleased |
| BF-31f4517 | Radius gateway and OAuth authorization URLs are parsed and serialized with the WHATWG URL parser, as Node's… | PiG's own defect | All platforms | 0.5.0 |
| BF-3270e5f | llama-cpp-classify answers match Pi bit for bit: the softmax uses jsmath.Exp, V8's fdlibm Math.exp… | PiG's own defect | All platforms | 0.5.0 |
| BF-32ae04c | Durable (Pi 89e5c2c0): a generation waiting on a blocking threshold compaction fails with model_error… | PiG's own defect | All platforms | 0.5.0 |
| BF-32b5367 | A running !command that prints a lot, the /resume search over many long sessions, and streamed answers… | PiG's own defect | All platforms | 0.5.0 |
| BF-32ce7ad | The Termux page shows the [Context] line as PiG prints it, ~/.pig/agent/AGENTS.md | PiG's own defect | Android and Termux | 0.5.0 |
| BF-32e86a8 | Go, Python and Rust SDK: theme.bold, italic, underline, inverse and strikethrough now draw as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-334bb4b | A Gemini or Mistral stream chunk with a member of an unexpected type no longer sorts the members of a tool… | PiG's own defect | All platforms | 0.5.0 |
| BF-3351167 | The OAuth callback server reads an absolute-form request target with a scheme other than http or https, a… | PiG's own defect | All platforms | 0.5.0 |
| BF-33dc8a0 | fix(ai): keep a custom client identity's slash in Bedrock's user agent, as the JavaScript SDK does | PiG's own defect | All platforms | Unreleased |
| BF-33ea9e4 | fix(sdk): name the Go ModelRegistry.Find change in the upgrade rules; Node adoption survives an unbound leaf… | PiG's own defect | All platforms | Unreleased |
| BF-33fe10d | ModelsError appends the cause detail the way Pi does: an Error cause with an empty message contributes its… | PiG's own defect | All platforms | 0.5.0 |
| BF-33fe10d-2 | Live-Pi oracles TestExtensionSelectorMatchesPi (4,320 probes) and TestExtensionInputMatchesPi (1,144… | PiG's own defect | All platforms | 0.5.0 |
| BF-3452432 | Escape cancels a running ! or !! command again, as in Pi: the command stops and its block ends with… | PiG's own defect | All platforms | 0.5.0 |
| BF-34aa421 | fix(agent): hand the stream function Pi's messages; repair the tool flow in the provider path | PiG's own defect | All platforms | Unreleased |
| BF-34b8e92 | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-34bcd58 | OpenAI Responses prices a response that reports no service_tier at the requested tier, as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-34c4d8e | MCP tools declare the CallToolResult scripts receive as Pi 1.2.0 does: content blocks with their type… | PiG's own defect | All platforms | 0.5.0 |
| BF-34c4d8e-2 | The MCP extension decides which tools reach MCP tools by their ReachesTools, as Pi 1.2.0 does: another… | PiG's own defect | All platforms | 0.5.0 |
| BF-34f7594 | An extension that calls ctx.shutdown() from a session_start handler, or from a session_before_switch or… | PiG's own defect | All platforms | 0.5.0 |
| BF-34f7594-2 | Fixed the interactive bash block stripping escape sequences with the wrong rule… | PiG's own defect | All platforms | 0.5.0 |
| BF-34f7594-3 | Fixed the re: mode of the session selector search to read its pattern as JavaScript does (`new… | PiG's own defect | All platforms | 0.5.0 |
| BF-34f7594-4 | Repeated codemode timeouts or aborts can no longer pile up running VMs: at most two codemode VMs are alive… | PiG's own defect | All platforms | 0.5.0 |
| BF-3548f29 | fix(tui): bash block strips ANSI with Pi's utils/ansi.ts rule | PiG's own defect | All platforms | Unreleased |
| BF-35e46b1 | Codemode output and returned values keep a nested Error's name and message, with its own enumerable fields… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-364cc57 | RPC model objects of built-in models match Pi's: each keeps its catalog entry's own member order and spelling… | PiG's own defect | All platforms | 0.5.0 |
| BF-3651a90 | A provider stream that keeps sending keepalive comments or pings but no event now ends with an error after… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-369b1e1 | grep with context no longer loads each matched file into memory | PiG's own defect | All platforms | 0.5.0 |
| BF-36e192b | fix(lint): port Kitty chunking in UTF-16 units; clear the codemode, terminal image and exit-restore lint hits | PiG's own defect | All platforms | Unreleased |
| BF-36e82ad | fix(durable): a non-finite number is a strict JSON error whichever JSON package a value's own encoder used | PiG's own defect | All platforms | Unreleased |
| BF-37c6048 | fix(tui): a streamed Markdown never resumes past a paragraph clipped before a LaTeX start | PiG's own defect | All platforms | Unreleased |
| BF-37d2daa | An extension's ctx.model and model lists keep a model's cost.tiers when the model's record has it as an… | PiG's own defect | All platforms | 0.5.0 |
| BF-3804a5a | The pi-messages and classifier (System One, decisions) requests are sent to the URL as written and a URL with… | PiG's own defect | All platforms | 0.5.0 |
| BF-3840820 | MCP tool results whose content blocks carry members of another type than the protocol's (a numeric text, a… | PiG's own defect | All platforms | 0.5.0 |
| BF-38b0f0c | Syntax highlighting keeps its colors after the first line of multiline strings and comments in fenced code… | PiG's own defect | All platforms | 0.5.0 |
| BF-397866f | A Go MCP client that declares an empty capability, such as sampling or elicitation, now sends it as {} | PiG's own defect | All platforms | 0.5.0 |
| BF-3a0d0e1 | A Provider object that an extension registers while it loads is now applied after every extension has… | PiG's own defect | All platforms | Unreleased |
| BF-3ad9ec8 | fix(tui): tree label prompt gives its input width-2 cells (Pi LabelInput) | PiG's own defect | All platforms | Unreleased |
| BF-3b009a3 | chord/host: a child host forked by ForkDriver no longer inherits its parent's CHORD2_LOCK | PiG's own defect | All platforms | 0.5.0 |
| BF-3b3dba6 | A streamed tool argument of -0 is held as 0, so the serialized arguments write 0 as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-3b8aa66 | A Piglet Binary that strips bedrock-converse-stream links the AWS SDK again: the Bedrock retry backoff… | PiG's own defect | All platforms | 0.5.0 |
| BF-3ba74f2 | fix(codingagent): wait for a read's rest on the route snapshot that found no modal | PiG's own defect | All platforms | Unreleased |
| BF-3cb7131 | fix(sdk-py): stage pig_sdk.tui with the package, which pig_sdk imports at load; keep the pi-ai patterns… | PiG's own defect | All platforms | Unreleased |
| BF-3ce6bcb | fix(changelog,source): /i scheme tests fold as JavaScript does, not as RE2's (?i) | PiG's own defect | All platforms | Unreleased |
| BF-3cf03c0 | A faux provider error message that carries a response id writes responseId after errorMessage in session… | PiG's own defect | All platforms | 0.5.0 |
| BF-3d12e48 | fix(tui): tree selector cursor follows Pi's selectedIndex over an empty list | PiG's own defect | All platforms | Unreleased |
| BF-3d475c4 | Image resize strategy against live Pi: new TestResizeImageDimensionStrategyMatchesPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-3def2a7 | MCP servers whose names are numbers ("9", "42") load and report their errors in the order Pi does: a JSON… | PiG's own defect | All platforms | 0.5.0 |
| BF-3e755ef | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-3ee123c | CI: the Linux / portmap job (make portmap-check) no longer runs out its 60-minute limit | PiG's own defect | Linux | 0.5.2 |
| BF-3fa6e63 | MCP servers with resources registered no tools until every resource was listed, so a slow listing (a large… | PiG's own defect | All platforms | 0.5.0 |
| BF-3fbdfd2 | A configured number in settings text prints as JavaScript's String(number): -0 is 0, and a magnitude of… | PiG's own defect | All platforms | 0.5.0 |
| BF-4027b34 | A finished code-mode execution no longer keeps its closed VM reachable through its stopped halt-grace timer… | PiG's own defect | Linux | 0.5.0 |
| BF-4058f34 | The /fork message picker checks its keys in Pi's order (up, down, confirm, cancel) when bindings overlap | PiG's own defect | All platforms | 0.5.0 |
| BF-40b5a89 | Assistant messages in session files and RPC output list responseId before responseModel, Codex's… | PiG's own defect | All platforms | 0.5.0 |
| BF-40bc651 | The Anthropic event reader decides message_start and message_stop from the parsed event type as Pi does (not… | PiG's own defect | All platforms | 0.5.0 |
| BF-410d733 | settings.json, trust.json and auth.json are written as Pi's JSON.stringify writes them: <, > and… | PiG's own defect | All platforms | 0.5.0 |
| BF-411a8cb | Background runs of pi-subagents (and other helpers an extension starts as a detached Node process) complete… | PiG's own defect | All platforms | 0.5.0 |
| BF-4163646 | AuthStorage file semantics: new live-Pi oracle TestAuthStorageFileMatchesPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-41b305d | Codemode rejects a // @options: line that is not the first line of the script with an error before the… | PiG's own defect | All platforms | 0.5.0 |
| BF-42116f8 | Durable: the system baseline written after a compaction now leads the model context when the kept history… | PiG's own defect | All platforms | 0.5.0 |
| BF-4259631 | An extension's direct call into a pi-ai API module (openAICompletionsApi() and the other bridged APIs)… | PiG's own defect | All platforms | 0.5.0 |
| BF-42c059a | fix(tui): bash block strips ANSI with Pi's utils/ansi.ts rule | PiG's own defect | All platforms | Unreleased |
| BF-42e3d0e | fix(hljs): a class that negates only \r still reaches across lines; gate the streamed code block Linear | PiG's own defect | All platforms | Unreleased |
| BF-4312595 | fix(ai,durable): keep a value that is not JSON an error when a message or a nested result is encoded | PiG's own defect | All platforms | Unreleased |
| BF-432ab7f | An agent assistant message that holds an empty diagnostics array writes it as [], as Pi does, instead of… | PiG's own defect | All platforms | 0.5.0 |
| BF-433d232 | A request sent after /tree navigation to a point between parallel tool calls and their results no longer… | PiG's own defect | All platforms | 0.5.0 |
| BF-433d232-2 | An extension custom message whose content is a block array (text or images), or an empty string, now reaches… | PiG's own defect | All platforms | 0.5.0 |
| BF-433d232-3 | DefaultResourceLoader.reload: new live-Pi oracle TestDefaultResourceLoaderReloadMatchesPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-433d232-4 | ReadOnlyAuthStorage: new live-Pi oracle TestReadOnlyAuthStorageMatchesPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-43a2293 | Key order of non-index keys stays byte order (a Go map has no insertion order): question Q-state-order filed | PiG's own defect | All platforms | Unreleased |
| BF-43c46f5 | Chord host timers (reload vote, gate and retire deadlines, link pings, service linger, host manager grace… | PiG's own defect | All platforms | 0.5.0 |
| BF-43d2597 | @ file autocomplete finds paths for queries that hold regex metacharacters such as [ or (, for a query… | A fix Pi shipped that PiG ported | All platforms | 0.5.2 |
| BF-43d2597-2 | /compact <instructions> applies the instructions to the summary of the start of a turn that compaction… | A fix Pi shipped that PiG ported | All platforms | 0.5.2 |
| BF-43d2597-3 | A resumed session and /tree navigation activate the extension tools the branch never added or removed, such… | A fix Pi shipped that PiG ported | All platforms | 0.5.2 |
| BF-43d2597-4 | Switching to a session whose system message holds a toolsAdded or toolsRemoved member the loadout restore… | A fix Pi shipped that PiG ported | All platforms | 0.5.2 |
| BF-448d2a9 | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-448ff71 | A panic in an extension error listener is now named on stderr instead of vanishing, and extension SDK… | PiG's own defect | All platforms | 0.5.0 |
| BF-448ff71-2 | MCP notifications no longer swallow every panic from the notify callback; only the "MCP failed to load"… | PiG's own defect | All platforms | 0.5.0 |
| BF-44f8d86 | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-4599fab | fix(ai): an iteration's shared cancellation does not compare contexts | PiG's own defect | All platforms | Unreleased |
| BF-45ec149 | Read-only credential storage reports an unreadable or malformed auth.json as Pi does: `Failed to read… | PiG's own defect | All platforms | 0.5.0 |
| BF-4610c9f | A ! or !! command's block draws the bytes Pi's BashExecutionComponent draws: its rows are padded to the… | PiG's own defect | All platforms | 0.5.0 |
| BF-4649166 | fix(sdk): place the arrival-order start in the request scope so a tool_call round trip keeps its 68… | PiG's own defect | All platforms | Unreleased |
| BF-46abed3 | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-471c46e | An MCP tool or resource link with an explicit empty title keeps it, as Pi does, instead of falling back to… | PiG's own defect | All platforms | 0.5.0 |
| BF-4789fa5 | thinking_level_select and turn_start handlers are covered through the production Session: a handler sees… | PiG's own defect | All platforms | 0.5.0 |
| BF-47c3007 | /mcp now opens at once while servers are still connecting, and connection actions (reconnect, enable… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-47c3007-2 | MCP OAuth sign-ins can now be cancelled with Esc at every step, session shutdown stops a running sign-in, and… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-47c3007-3 | Shutdown no longer waits up to 15 seconds to refresh an MCP OAuth token that was about to expire, only to… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-48318eb | fix(hljs): a resumed run counts the scan iterations before its checkpoint | PiG's own defect | All platforms | Unreleased |
| BF-48435cd | A parsed http, https, ws, wss or ftp URL reports its fragment percent-encoded, as Node's url.hash does | PiG's own defect | All platforms | 0.5.0 |
| BF-486aee5 | fix(sdk): the Go SDK ends the connection on a frame it cannot decode | PiG's own defect | All platforms | Unreleased |
| BF-488a47a | chord/host: ProcessLock no longer fails with invalid argument on macOS and the BSDs when the temporary… | PiG's own defect | macOS | 0.5.0 |
| BF-48a28fe | Models an extension's provider object returns keep the member order the extension wrote them in, as Pi keeps… | PiG's own defect | All platforms | 0.5.0 |
| BF-495f017 | A bashExecution entry records excludeFromContext: false when the caller passed false, as Pi does; an… | PiG's own defect | All platforms | 0.5.0 |
| BF-495f017-2 | A branch-summary entry writes fromHook: false in Pi's member order, and the summary has its Spacer | PiG's own defect | All platforms | 0.5.0 |
| BF-4a0bc3d | The footer context percentage is refreshed after a ! command is recorded, as Pi's footer asks the session… | PiG's own defect | All platforms | 0.5.0 |
| BF-4ac21c8 | fix(jsloop): run a tick whose tool call already ended at once, though no wait is open | PiG's own defect | All platforms | Unreleased |
| BF-4afdbac | ProjectTrustStore reads entries in JavaScript order: readTrustFile named whichever invalid value Go's map… | PiG's own defect | All platforms | 0.5.0 |
| BF-4bcc7da | StreamProxy merges the final tool call and reports abort and timeout as Pi does, and it no longer changes a… | PiG's own defect | All platforms | 0.5.0 |
| BF-4c45afa | fix(sdk-py): stage pig_sdk.tui with the package, which pig_sdk imports at load; keep the pi-ai patterns… | PiG's own defect | All platforms | Unreleased |
| BF-4c7ccf2 | A classifier response that reports more than 2,147,483,647 input or output tokens keeps the reported count… | PiG's own defect | All platforms | 0.5.0 |
| BF-4d04134 | The cancel hint of extension loaders (/share, /bug) shows the key bound to tui.select.cancel instead of… | PiG's own defect | All platforms | 0.5.0 |
| BF-4d33f78 | fix(tui): a tool error result collapses like any other (Pi updateResult) | PiG's own defect | All platforms | Unreleased |
| BF-4d965e3 | The /tree label of a custom tool call shows its arguments in the order the model sent them, with <, >… | PiG's own defect | All platforms | 0.5.0 |
| BF-4e204f4 | fix(sdk-py): clear bandit's findings in the 0.5.2 Python SDK code | PiG's own defect | All platforms | Unreleased |
| BF-4e47260 | A lazy line joins a list item as written (indentation kept), four spaces after the marker make the first line… | PiG's own defect | All platforms | 0.5.0 |
| BF-4e5b684 | A list item whose first line is another list item is rendered as a nested list with no bullet of its own, and… | PiG's own defect | All platforms | 0.5.0 |
| BF-4e8c517 | fix(runtimecell): keep the Python runner's _run callable without the built event | PiG's own defect | All platforms | Unreleased |
| BF-4f079a7 | An MCP request over Streamable HTTP that is cancelled or times out while the server's authorization is being… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-4f6992d | Durable: a tool's PrepareArguments result of a typed object (a struct, or a map of another element type)… | PiG's own defect | All platforms | 0.5.0 |
| BF-4f6992d-2 | Durable: a PrepareArguments result that is not an object fails validation as an invalid_arguments tool… | PiG's own defect | All platforms | 0.5.0 |
| BF-4fab2ef | Durable error values (FileError, ExecutionError, JsonlCorruptionError, ReadAfterWrite, StorageRejected… | PiG's own defect | All platforms | 0.5.0 |
| BF-4fc8c2e | With an active Piglet, an extension that denies every tool with SetActiveTools([]) is no longer widened… | PiG's own defect | All platforms | 0.5.0 |
| BF-4fe779e | A /reload that arrived while the supervisor was restarting a crashed isolated extension could end the… | PiG's own defect | All platforms | 0.5.2 |
| BF-50233fd | An env SSH command that runs past its timeout now receives SIGTERM, as in Pi, instead of SIGKILL, and the… | PiG's own defect | All platforms | 0.5.0 |
| BF-504869b | The MCP client uses JavaScript's trim() and \s where Pi does: in stdio and Streamable HTTP blank-line and… | PiG's own defect | All platforms | 0.5.0 |
| BF-504869b-2 | MCP response headers are read as Node's fetch returns them, one Latin-1 character per byte | PiG's own defect | All platforms | 0.5.0 |
| BF-504869b-3 | A JSON-RPC number id is a JavaScript number | PiG's own defect | All platforms | 0.5.0 |
| BF-50f526d | The Go, Rust and Python SDKs export Pi's CURRENT_SESSION_VERSION, VIRTUAL_MODEL_STATE_ENTRY… | PiG's own defect | All platforms | 0.5.0 |
| BF-5144418 | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-5260e5c | The Rust SDK's get_model_info() reads Pi's maxTokens and cost (it reported 0 for the output limit and… | PiG's own defect | All platforms | 0.5.0 |
| BF-52858aa | setWorkingMessage with no message restores the default working label and an empty string shows an empty… | PiG's own defect | All platforms | 0.5.0 |
| BF-53385a3 | Registering a large MCP catalog, building a CodeMode sandbox from one, and CodeMode tool discovery were… | PiG's own defect | All platforms | 0.5.2 |
| BF-5380e0a | fix(extension): load failure shows the extension's own cause; registration edge-case rows | PiG's own defect | All platforms | Unreleased |
| BF-54283ce | fix(extension): Shutdown closes live child sessions and releases the objects they hold | PiG's own defect | All platforms | Unreleased |
| BF-542b536 | fix(cli): PIG_VERSION_FORMAT=pi prints the pinned Pi package version | PiG's own defect | All platforms | Unreleased |
| BF-5443b6c | durable/images is the image processor of pi-durable: images.CreatePhotonImages() (also images/node and… | PiG's own defect | All platforms | 0.5.0 |
| BF-54a5bf6 | The host answers a parsed frontmatter with <, >, & and U+2028 as they are (as JSON.stringify writes… | PiG's own defect | All platforms | 0.5.0 |
| BF-5654e71 | MCP OAuth discovery and resource selection now read URLs the way Node's URL does (default ports, dot… | PiG's own defect | All platforms | 0.5.0 |
| BF-5654e71-2 | A JSON-RPC request or notification with a malformed error member is no longer rejected, and a method that… | PiG's own defect | All platforms | 0.5.0 |
| BF-56731e9 | /login runs a provider object's OAuth login with Pi's login interaction, so an info event shows in the… | PiG's own defect | All platforms | 0.5.0 |
| BF-56731e9-2 | An extension's API-key login no longer shows "Waiting for authentication..." after an authorization URL | PiG's own defect | All platforms | 0.5.0 |
| BF-56a0400 | Single-line inputs (login prompts, search boxes) ignore xterm modifyOtherKeys text sequences, as Pi's Input… | PiG's own defect | All platforms | 0.5.0 |
| BF-56f1d3c | A family document member accepts a typed Go value as its seed, as CreateTask input and snapshots already do | PiG's own defect | All platforms | 0.5.0 |
| BF-56f1d3c-2 | Submission.Wait, WaitForTask and the idle waits return after every commit subscriber has run for the… | PiG's own defect | All platforms | 0.5.0 |
| BF-570f857 | Fixed interactive sessions leaving extension prompt sections, such as the MCP mcp_servers list, out of the… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-5732479 | auth.json keeps each credential's properties in the order they were read or returned by an extension's… | PiG's own defect | All platforms | 0.5.0 |
| BF-5774749 | A Piglet found by name in <project>/.pig/piglets now follows project trust: it is used only when the… | PiG's own defect | All platforms | 0.5.0 |
| BF-578c1a3 | The "Applied use cases" page in the manual that ships inside PiG now links the Security page on the website | PiG's own defect | All platforms | 0.5.0 |
| BF-579b4d2 | fix(durable): a non-finite number is a strict JSON error whichever JSON package a value's own encoder used | PiG's own defect | All platforms | Unreleased |
| BF-57a1687 | fix(ai,agent): return a projection's blocks apart from its scalars so the caller's buffer stays on its stack | PiG's own defect | All platforms | Unreleased |
| BF-57ee33b | A model an extension registers with registerProvider reaches ctx.modelRegistry, ctx.model and RPC as… | PiG's own defect | All platforms | 0.5.0 |
| BF-59456ae | Pressing a key in a subprocess extension's overlay no longer freezes interactive mode when the key reaches… | PiG's own defect | All platforms | 0.5.0 |
| BF-598e27f | Escape over an open autocomplete list during compaction now closes the list and lets compaction continue, as… | PiG's own defect | All platforms | 0.5.0 |
| BF-59fb623 | fix(requestvalidity): one Responses assistant turn holds all its items, in any order | PiG's own defect | All platforms | Unreleased |
| BF-5a093cd | In print and JSON mode, Node extensions that share one process now keep Pi's order at disposal | PiG's own defect | All platforms | 0.5.0 |
| BF-5aab94c | The color level check reads TERM with JavaScript's case-insensitive matching, as Pi's chalk does: a name… | PiG's own defect | All platforms | 0.5.0 |
| BF-5ab2304 | Starting an extension no longer fails with fork/exec …: text file busy when the host builds or starts… | PiG's own defect | Linux | Unreleased |
| BF-5b0e909 | /scoped-models: an empty search no longer makes enable-all/clear-all act on every model, clearing every model… | PiG's own defect | All platforms | 0.5.0 |
| BF-5b15407 | In-process extensions that call pi.getCommands() now receive the extension commands, prompt templates and… | PiG's own defect | All platforms | 0.5.0 |
| BF-5b1d5b1 | A resource pattern that starts with ~ (!~/claude-backup/skills/pdf, +~/…, -~/…, wildcards) now… | PiG's own defect | All platforms | 0.5.0 |
| BF-5b4a3d3 | A lazy line no longer continues a quote whose last quoted line is empty (>\nnext is an empty quote then a… | PiG's own defect | All platforms | 0.5.0 |
| BF-5bc4773 | RPC set_model applies after its auth check, as Pi's awaited setModel does, so a cycle_model in the same… | PiG's own defect | All platforms | 0.5.0 |
| BF-5bdbf20 | fix(cli): keep scratch install output off the validate-only report | PiG's own defect | All platforms | Unreleased |
| BF-5c4167a | fix(extension): hold the theme colors map the wire palette memo is keyed on | PiG's own defect | All platforms | Unreleased |
| BF-5c9d23f | A provider object's OAuth login receives Pi's login options, as oauth.login(interaction, options) does in Pi | PiG's own defect | All platforms | 0.5.0 |
| BF-5d004ae | fix: give Windows git children core.longpaths through the environment, keeping Pi's argv | PiG's own defect | Windows | Unreleased |
| BF-5d36a58 | fix(agent): end the tool flow at custom user messages and hold system messages mid-flow | PiG's own defect | All platforms | Unreleased |
| BF-5d6bb25 | The request-string walk that splices cached strings into a request body finds a cycle by the pointer, map or… | PiG's own defect | All platforms | 0.5.0 |
| BF-5d8eda1 | pig auth check and pig auth print-api-key no longer reject --api-key that another option takes as its… | PiG's own defect | All platforms | 0.5.0 |
| BF-5dbfed1 | A scripted faux response can carry DurationMs | PiG's own defect | All platforms | 0.5.0 |
| BF-5e36d87 | The assistant-message frame encoder no longer re-parses and re-serializes a whole tool call's arguments for… | PiG's own defect | All platforms | 0.5.0 |
| BF-5e3e346 | An explicit anthropic-beta header, from a model's headers or the request, no longer makes a… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-5eab79d | processImage against live Pi: new TestProcessImageMatchesPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-5ec5f5f | The built-in bash, read and list tool cards now show an image block that has no MIME type as `[Image… | PiG's own defect | All platforms | 0.5.0 |
| BF-5f03fa4 | An extension that a child agent session's resource loader loads now receives the session's events, as in Pi… | PiG's own defect | All platforms | 0.5.2 |
| BF-5f03fa4-2 | The contexts of those extensions read the child session: ctx.model, ctx.scopedModels (the session's… | PiG's own defect | All platforms | 0.5.2 |
| BF-5f22b85 | fix(coding): shut down the extension host of a DefaultResourceLoader an extension constructed when its last… | PiG's own defect | All platforms | Unreleased |
| BF-60c4c6f | A tool call whose finished arguments are not a valid JSON object no longer runs with the parsed prefix: its… | PiG's own defect | All platforms | 0.5.0 |
| BF-612955a | The Rust and Go SDKs keep an unpaired surrogate through prepareBranchEntries… | PiG's own defect | All platforms | 0.5.0 |
| BF-61300bb | MCP OAuth reads a non-HTTP redirect or server URL as Node's new URL does: dot segments are removed, a… | PiG's own defect | All platforms | 0.5.0 |
| BF-61300bb-2 | Sorting names with localeCompare (MCP servers, sessions, login providers, codemode namespaces, model lists… | PiG's own defect | All platforms | 0.5.0 |
| BF-6132277 | An MCP OAuth authorization response with an iss parameter is now rejected when no authorization server… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-61fc01d | list_mcp_resources, list_mcp_resource_templates and read_mcp_resource return every member a server sent… | PiG's own defect | All platforms | 0.5.0 |
| BF-61fc01d-2 | MCP tool descriptions, resource MIME types, a pasted sign-in redirect URL and the resource tools' server… | PiG's own defect | All platforms | 0.5.0 |
| BF-6235532 | Filterable selector lists (theme, images, tree-filter-like pickers) check up/down before confirm/cancel like… | PiG's own defect | All platforms | 0.5.0 |
| BF-6258d27 | fix(rpc): a replacement aborts the compactions of its batch at its teardown only | PiG's own defect | All platforms | Unreleased |
| BF-625a32e | Display text that PiG cuts at a length (the /tree listing, the Piglet system-prompt preview, an extension… | PiG's own defect | All platforms | 0.5.0 |
| BF-626dccc | OpenAI-compatible Chat Completions requests resolve chat_template_args (the baseten thinking format) and… | PiG's own defect | All platforms | 0.5.0 |
| BF-62a1c87 | The ExtensionSelector and ExtensionInput oracles also run with the Kitty keyboard protocol active (CSI-u… | PiG's own defect | All platforms | 0.5.0 |
| BF-62ae30d | Durable: reading the history of a conversation whose parent conversation is not stored now fails with Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-62c3691 | grep with a large context no longer holds every context line in memory: its memory is bounded by the 50KB… | PiG's own defect | All platforms | 0.5.0 |
| BF-6323c8e | fix(ai): a Bedrock model outside the catalog is a bedrock-converse-stream model of amazon-bedrock, so its own… | PiG's own defect | All platforms | Unreleased |
| BF-63b4fc8 | NodeExecutionEnv.Watch gives a directory replaced at the same path a new watcher, reports changes to the… | A fix Pi shipped that PiG ported | macOS | 0.5.0 |
| BF-63bb0b7 | pig extension upgrade now rewrites a Context getter call that is the only argument of a variadic call… | PiG's own defect | All platforms | 0.5.0 |
| BF-644a9f2 | Changelog link rewriting and git source parsing match a URL scheme case-insensitively as Pi's regex with… | PiG's own defect | All platforms | 0.5.0 |
| BF-644a9f2-2 | A Node extension's ctx.ui.custom factory can call tui.showOverlay, tui.hideOverlay and tui.setFocus… | PiG's own defect | All platforms | 0.5.0 |
| BF-644b3e0 | A codemode script that calls text() tens of thousands of times no longer blocks the process: adjacent text… | PiG's own defect | All platforms | 0.5.0 |
| BF-648ff3e | A child session's message_update event carries the message so far and the assistant event's partial, as… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-10 | pig --list-models now prints only the models the model runtime reports as available, as Pi does | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-100 | An SDK's ctx.hasPendingMessages() in interactive mode counts queued user messages only, as Pi does, and no… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-101 | A Node extension that changes the active tools or registers a tool now reads the system prompt back as Pi does | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-102 | ctx.isIdle() after pi.sendMessage(..., { triggerTurn: true }) in an agent_settled handler of a Node… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-103 | A Node extension's ctx.ui.select, confirm and input honor options.signal, as Pi's RPC and interactive… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-104 | A start could fail with a bare missing-file error when another pig's cache collection removed the cached… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-105 | A tool's prepareLoadout hook now runs again before every later turn of a run, as in Pi, so the descriptions… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-106 | Fixed the chord host keeping a disposed object reachable until its ready timeout (a minute by default) had… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-107 | The /tree "Summarize branch?" selector and "Custom summarization instructions" editor, the /import… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-108 | OpenAI Responses requests keep the prompt cache across tool changes (Pi #11142) | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-109 | On a file system that refuses file locks (an NFS mount without a lock manager, a FUSE or SMB share), starting… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-11 | /reload no longer reports `Extension "builtin:codemode" error: This extension ctx is stale after session… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-110 | In RPC mode, a toast an extension deferred with setTimeout(0) from a model_select or session_start… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-111 | In RPC mode, a message an extension's session_start listener sent while a command replaced the Session was… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-112 | In RPC mode, an extension command that sends a user message and then awaits ctx.waitForIdle() (the init… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-113 | Two Bun runtime installs, or an install and a status check, in different processes no longer leave a moment… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-114 | A slow toolchain version check no longer blocks other starts without limit | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-115 | Fixed the chord host manager letting other code read it as stopped, or start the child again, before the… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-116 | Fixed a chord link closed with a reason reporting "peer closed" to its close handlers instead when the peer… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-119 | Concurrent extension select, confirm, input, editor dialogs and inline custom() components in… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-12 | Prompts submitted after /scoped-models refreshed the model catalogs and Ctrl+P switched the model no longer… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-120 | Pasting in the shell no longer hangs after copying text in pig and suspending it with Ctrl+Z on Linux: the… | PiG's own defect | Linux | 0.5.2 |
| BF-648ff3e-123 | mcp.json files that start with a UTF-8 BOM, which Windows PowerShell 5.1 adds when it rewrites a file, load… | PiG's own defect | Windows | 0.5.2 |
| BF-648ff3e-124 | A Go or Rust extension edited while it was being built could be cached under the hash of the source from… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-125 | A Node extension that registers or unregisters a provider through ctx.modelRegistry (or… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-126 | ctx.abort() aborts the run's ctx.signal, and the signal a tool receives, before it returns in a Node… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-127 | pi.getCommands() in a Node extension lists a command registered after load by the same turn's… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-128 | A Node extension tool that awaits real work and then takes the file mutation queue (the upstream subagent… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-129 | RPC mode: an extension_ui_response whose value is not a string, whose confirmed is not a boolean, or… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-13 | Two parallel edit calls through two names of one existing file (a hard link, or a case alias on a… | A Pi defect that PiG fixed first | macOS | 0.5.2 |
| BF-648ff3e-130 | A YAML date used as a mapping key is named with the time zone names of Node 24.22 and later, the Node Pi runs… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-14 | The Cloudflare host's pi-durable facade sends Pi v1.4.0's client identity in every model request's options… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-15 | The Go, Rust and Python extension SDKs' isContextOverflow treats an HTTP 413 error as a context overflow… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-16 | The Go, Rust and Python extension SDKs' isRetryableAssistantError retries on a status code (429, 500, 502… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-17 | CI: TestNodeCellAddedMembersStayWithinTheirOwnState no longer fails under load | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-18 | CI: a Node extension no longer holds a request the host sends it behind the highlight.js language load | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-19 | CI: waitForRenderProxy, the wait of the render proxy and packed padding tests for the extension's answer… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-2 | A streamed child-session text costs wire bytes linear in its length: message_update events travel as a… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-20 | CI: TestStartupReusesPreTrustExtensionsAcrossEntrypoints no longer reads an extension process that is still… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-21 | CI: the scoped-model selector parity scenarios no longer capture the selector before the catalog refresh… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-22 | CI: a Node extension no longer holds the first request the host sends it behind its bytecode-cache flush | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-23 | CI: TestTerminalProgramGetsDefaultSignalsWhenTheDaemonStartedIgnoringThem no longer fails every run under… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-24 | A package manifest glob walks as Node 24.22 and later walk it | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-25 | An HTTP 413 from any provider is treated as request-size overflow, so the agent compacts and retries instead… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-26 | On Windows, PiG finds PowerShell in its installation directories when it is not on PATH: PowerShell 7 under… | PiG's own defect | Windows | 0.5.2 |
| BF-648ff3e-27 | On Windows, the legacy WSL bash.exe launcher is recognized under the actual Windows directory in… | PiG's own defect | Windows, WSL | 0.5.2 |
| BF-648ff3e-28 | On Windows, an empty SystemRoot takes C:\Windows for taskkill.exe, as an unset one already did, in the… | PiG's own defect | Windows | 0.5.2 |
| BF-648ff3e-29 | On Windows, shells that are runnable Microsoft Store aliases are accepted: a configured shell path… | PiG's own defect | Windows | 0.5.2 |
| BF-648ff3e-3 | coding.CreateAgentSession binds the Session it returns to the Extension Host its resource loader started… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-30 | URL parsing follows Node 24.22 and later | PiG's own defect | Windows | 0.5.2 |
| BF-648ff3e-31 | A packed Go, Python or Rust cell no longer hangs when a later member's factory calls the host before an… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-32 | An extension's ctx.hasPendingMessages() now asks the Host instead of reading a cached state snapshot, so it… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-33 | Durable with telemetry off (no telemetry or the no-op span) no longer builds span attributes, events or… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-34 | The grep tool passes --no-config to ripgrep, as Pi does at 8c4e8738, so a user's RIPGREP_CONFIG_PATH… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-35 | Recovery of a truncated assistant attempt (retry and overflow/length compaction) finds the tool results that… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-36 | Classifier models take images (Pi 7f225f25): models.classify() accepts images for TypeSafe's Jev and… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-37 | The catalog tolerates the free field Pi v1.4.0's radius data carries (recursive/flint), keeping it in the… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-38 | The upstream pin moves to Pi v1.4.0 | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-39 | The durable turn classifier retries on a status code (429, 500, 502, 503, 504, 520, 524) only when it stands… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-4 | Extension tools of one parallel batch reach the host in Pi's order | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-44 | Automatic retry no longer treats digits inside a request id or another number (for example req_011CU503xyz… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-45 | Automatic retry now triggers for stream disconnected before completion errors that a proxy wrapping the… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-46 | Sign in with ChatGPT: a ChatGPT Business workspace usage-limit error… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-47 | Context and the types that hold one (EventBus, ModelRegistry, ReplacedSessionContext… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-48 | pi.events.emit in a Go, Rust or Python factory behaves as Pi's does: the first factory-time bus call… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-49 | The Python SDK test test_provider_transport_shutdown_does_not_settle_owned_stream[True-*] no longer fails… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-5 | The tool_result handlers of a parallel batch start in the order the tools settled, each running to its… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-50 | A Go extension SDK loop that is served without Run (a test on its own connection) no longer waits for a… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-51 | The Node extension API probe of a failed factory (TestUpstreamFailedFactoryDisablesCapturedAPI) names… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-52 | A listener a Go, Rust or Python factory subscribes hears another extension's emit made while the factory… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-53 | A command that an extension started with pi.exec and an AbortSignal kept running after the signal… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-54 | Extension API parity ledger: removed the stale ctx.ui.custom(options.onHandle) exception (Go, Rust and… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-55 | Conformance fixtures now reach the realized SDK export cells the export ledger listed as unreached: the… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-56 | Releasing a handle of an object a child session was created over (a ModelRuntime first of all) no longer… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-57 | Removing a local package reports start and complete remove progress events, as Pi's remove does for… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-58 | registerProvider of a model without an api fails with Pi's Set it at model level or provider level.… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-59 | An extension's SessionManager.appendContextEdit(id, null) clears the content instead of failing, and… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-6 | The Go, Python and Rust SDKs start the handlers of concurrent event and command requests in the order the… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-60 | Extension SDKs: formatKeyText/keyDisplayText upper-cased the first rune of a part, where Pi's… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-61 | Extensions: ctx.ui.setTheme(theme) with a theme object makes that object the active theme as it is, as Pi's… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-62 | A PiG TLS client no longer accepts a certificate chain through a compromised eMudhra intermediate or a root… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-63 | The CI images no longer ship the three removed emSign roots: ca-certificates 20261020-r0 in the ci-go lock… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-64 | Shutting a host down while a child session is live no longer leaves its extension host running | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-65 | Extensions: in a packed Go, Rust or Python cell, a member that stops or is replaced no longer releases a file… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-66 | Extensions: withFileMutationQueue is now one queue for the built-in edit and write tools and every… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-67 | A Python extension's ctx.session_name and a Rust extension's ctx.session_name() returned an empty string… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-68 | ctx.model is the model's id in every SDK, as in Pi (ctx.model.id) | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-69 | A read of the session name, the session log or the model right after setLabel, setSessionName… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-7 | An edit or write call of a parallel batch registers in the file mutation queue after the extension tools… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-70 | An extension that reads ctx.model, pi.getSettings(), pi.getSessionName(), pi.getThinkingLevel() or… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-71 | In a Rust extension's before_agent_start handler, ctx.get_system_prompt() returns the prompt the… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-72 | RPC mode no longer writes entry_appended for a pi.appendEntry or session_info_changed for a… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-73 | Docs: another PiG build that shares the config root no longer removes the documentation bundle a running… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-74 | RPC mode now writes the members of an extension_ui_request that Pi writes empty, as Pi's JSON.stringify… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-75 | Starting a second Piglet Binary instance no longer fails on Windows while the first is running | PiG's own defect | Windows | 0.5.2 |
| BF-648ff3e-76 | RPC mode writes one setWidget request per ctx.ui.setWidget call from the Go, Python and Rust SDKs, in… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-77 | In print and JSON mode, a /skill:name prompt whose skill file cannot be read now reports the skill's read… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-78 | Images are now resized with the image profile of the model that answered last under a virtual model selection | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-79 | The system prompt a run forced no longer shows while the bash and custom messages held during that run are… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-8 | The Node runtime reports a tool whose execute awaits something that is not a host call, and the host closes… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-80 | An extension that reads pi.getMcpServers() from a timer, thread or goroutine and has no… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-81 | An extension that reads ctx.ui.theme from a timer, thread or goroutine now sees the theme the user selects… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-82 | A Node extension that registers or unregisters a virtual model… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-83 | CI: the nightly -race run now covers the packages that own most of PiG's goroutines | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-84 | A pig session on another machine or container that shares your ~/.pig (an NFS home, a bind mount) is no… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-85 | The upstream test inventory records Pi's shell-path-exists.test.ts as ported instead of partial: Pi mocks… | PiG's own defect | Windows | 0.5.2 |
| BF-648ff3e-86 | Fixed a chord host root failing with superseded: code version N is not retained although it was started in… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-87 | Print mode and --mode json no longer run an extension command a second time on text an input handler or a… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-88 | Shutting down a host while a crashed extension waits out its restart backoff no longer leaves a goroutine… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-89 | Forking a session from a file no longer overwrites a session file that already has the new session's… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-9 | A Node extension tool that awaits the event loop before it takes the file mutation queue registers after the… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-90 | Extension SDKs: ModelRegistry.find reports a host or transport failure instead of "no such model" | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-91 | Python SDK: has_configured_auth and is_using_oauth answer False for a model with no provider, as Pi and… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-92 | Go SDK: Context.CancellationReason returns the reason the host gave when it cancelled the request, as the… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-93 | A compiled-in extension factory, inline or built in, no longer panics "Extension runtime not initialized"… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-94 | The interactive mode no longer rewrites every provider request's system prompt into one head | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-95 | Markdown bold and italic render next to CJK punctuation in the terminal, for example… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-96 | A message sent with deliverAs: "nextTurn" while a run was starting could join that run instead of the next… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-97 | Markdown bold and italic next to CJK punctuation render in HTML exports and in Markdown that Node extensions… | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-98 | An extension's UI closures now run only in the TUI, as in Pi | PiG's own defect | All platforms | 0.5.2 |
| BF-648ff3e-99 | Interactive mode now routes a message an extension sends with sendMessage while a run streams as Pi does | PiG's own defect | All platforms | 0.5.2 |
| BF-65426f3 | fix(rpc): pipelined set_models apply in input order, after the batch's synchronous responses | PiG's own defect | All platforms | Unreleased |
| BF-65932e6 | fix(extensions): run a tick call after the tool_execution_end of the tool call that queued its callback… | PiG's own defect | All platforms | Unreleased |
| BF-65932e6-2 | A notification, status or message that an extension queued with setImmediate or setTimeout from a tool… | PiG's own defect | All platforms | 0.5.2 |
| BF-663e84b | The built-in models page lists the real thinking levels (off, minimal, low, medium, high, xhigh, max) instead… | PiG's own defect | All platforms | 0.5.0 |
| BF-663e84b-2 | The built-in models page documents the models.json provider and model fields (models[], contextWindow… | PiG's own defect | All platforms | 0.5.0 |
| BF-663e84b-3 | The built-in docs gain the compaction summary format, branch summaries and extension hooks, the RPC, JSON… | PiG's own defect | All platforms | 0.5.0 |
| BF-663e84b-4 | The packages page says how to build and run pig-acp, which npm:@pi-in-go/pigpen-acp needs but does not… | PiG's own defect | All platforms | 0.5.0 |
| BF-6646adc | A durable transaction that settles a submission with a detail that is not strict JSON now counts as having… | PiG's own defect | All platforms | 0.5.0 |
| BF-666acff | Package filters, manifest !/+/- overrides, settings resource patterns and built-in extension overrides… | PiG's own defect | All platforms | 0.5.0 |
| BF-666acff-2 | A pattern that Pi cannot compile now fails where Pi fails: a local Package keeps only the resource kinds… | PiG's own defect | All platforms | 0.5.0 |
| BF-668b3fa | Fix the shared attribute-order store case: advance the clock before flush and compare metric row names only | PiG's own defect | All platforms | Unreleased |
| BF-66b0fc0 | The Anthropic workload-identity token endpoint check parses the base URL with the WHATWG parser, as the… | PiG's own defect | All platforms | 0.5.0 |
| BF-66db311 | The login dialog now holds TUI focus while it is shown, so the prompt cursor appears in the right place for… | PiG's own defect | All platforms | 0.5.0 |
| BF-6708fbb | When a Go, Rust or Python extension that shares a process registers under a different name than the one it… | PiG's own defect | All platforms | 0.5.0 |
| BF-677ded1 | An indented line after a quoted heading, rule or fence in a blockquote renders as the code block Pi shows | PiG's own defect | All platforms | 0.5.0 |
| BF-68a4ed2 | PageUp and PageDown move the selection by a page in selection lists and selectors (settings, model… | PiG's own defect | All platforms | 0.5.0 |
| BF-68a63f2 | A tool namespace registered with aliases: [] keeps the empty array in pi.getAllTools() and the replicated… | PiG's own defect | All platforms | 0.5.0 |
| BF-68b129d | /export to a directory that cannot be created on a read-only file system (such as macOS's root) reports… | PiG's own defect | macOS | 0.5.0 |
| BF-68ceee2 | Tool-argument validation compares JSON values as JSON Schema does: a number is never equal to a string | PiG's own defect | All platforms | 0.5.0 |
| BF-68d0628 | The Go, Rust and Python SDKs export Pi's getLanguageFromPath | PiG's own defect | All platforms | 0.5.0 |
| BF-68f8761 | insecure: true on a provider an extension registers with refreshModels now reaches the request transport | PiG's own defect | All platforms | 0.5.0 |
| BF-68f98d7 | Fixed the error message of a failed lazy API setup, such as a module load or auth failure, using its failure… | PiG's own defect | All platforms | 0.5.0 |
| BF-6992ffb | A Go, Python or Rust extension that calls appendEntry in a handler that already read the session log now… | PiG's own defect | All platforms | 0.5.0 |
| BF-69b9811 | Two PiG builds that share a config root (two installed versions, or stock PiG beside a Piglet Binary) no… | PiG's own defect | All platforms | 0.5.0 |
| BF-69c175b | fix(models): models.json entries keep exposed limits and cost; bound thinking-only messages in summaries; pin… | PiG's own defect | All platforms | Unreleased |
| BF-69f6341 | The Radius relay WebSocket URL is built with the WHATWG URL parser, as Pi's new URL does: gateway hosts are… | PiG's own defect | All platforms | 0.5.0 |
| BF-6a6fffa | A session message whose integer member, such as timestamp, a usage count or durationMs, is spelled with a… | PiG's own defect | All platforms | 0.5.0 |
| BF-6a6fffa-2 | formatToolCallWithArgs against live Pi: new TestFormatToolCallWithArgsMatchesPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-6a6fffa-3 | getTextOutput against live Pi: new TestGetTextOutputMatchesPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-6a6fffa-4 | Live-Pi oracles for utils/headers.ts: TestHeadersToRecordMatchesPi (800 header sequences over case… | PiG's own defect | All platforms | 0.5.0 |
| BF-6a6fffa-5 | The Skills page said your own skills live in ~/.pig/skills, that a skill's text is added to the system… | PiG's own defect | All platforms | 0.5.0 |
| BF-6a6fffa-6 | Live-Pi oracle TestOpenAICompletionsCompatAndCacheOptionsPayloadMatchesPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-6a6fffa-7 | The settings documentation now says that images.blockImages replaces each image sent to the model with… | PiG's own defect | All platforms | 0.5.0 |
| BF-6a6fffa-8 | In --mode json, SIGTERM or SIGHUP during a tool call now stops the output and the session file where Pi… | PiG's own defect | All platforms | 0.5.0 |
| BF-6a6fffa-9 | TestSettingsSelectorMatchesPi opens each row under the Kitty keyboard protocol with CSI-u keys; no… | PiG's own defect | All platforms | 0.5.0 |
| BF-6ac4c8c | Ctrl+V pastes clipboard text in Termux | A fix Pi shipped that PiG ported | Linux, Android and Termux | 0.5.0 |
| BF-6cb29f0 | Started from a directory reached through a symlink, pig install/remove/list/update resolve local… | PiG's own defect | All platforms | 0.5.0 |
| BF-6cb29f0-2 | Started from a directory reached through a symlink, /export <file>.jsonl and /bug now write and name… | PiG's own defect | All platforms | 0.5.0 |
| BF-6cb29f0-3 | Switching to or importing an empty session file from a directory reached through a symlink starts the session… | PiG's own defect | All platforms | 0.5.0 |
| BF-6ce1db1 | In print and JSON mode, SIGTERM and SIGHUP always leave their line in exit.log | PiG's own defect | All platforms | 0.5.0 |
| BF-6d0ec02 | Durable (Pi eb2fce43): a wait that closes a cycle through waits and held owners now fails the task's commit… | PiG's own defect | All platforms | 0.5.0 |
| BF-6d3cfc8 | A user_bash operations object from an extension is no longer released while a command is running through it | PiG's own defect | All platforms | 0.5.2 |
| BF-6d9b403 | The /config selector keeps the first-seen order of groups that tie on origin, scope and source (auto… | PiG's own defect | All platforms | 0.5.0 |
| BF-6debb12 | fix(train): gate repairs after merging train 3 and the ordering stack | PiG's own defect | All platforms | Unreleased |
| BF-6dfa6bf | go install github.com/MichaelKinsy/PiG/cmd/pig@<version> downloads a root module of 181 MB instead of 304 MB | PiG's own defect | All platforms | 0.5.2 |
| BF-6e6a0dd | A session message with a member of another JSON type than Pi writes is now kept, as Pi keeps it | PiG's own defect | All platforms | 0.5.0 |
| BF-6ee6eae | A Session delivers each event to its listeners the way Pi's AgentSession does | PiG's own defect | All platforms | 0.5.0 |
| BF-6f26a84 | Xiaomi MiMo models send only the thinking on/off toggle (no reasoning_effort) and expose only the off and… | PiG's own defect | All platforms | 0.5.0 |
| BF-6f4a82f | Validation errors for propertyNames name the failing member in their path, contains reports only "must… | PiG's own defect | All platforms | 0.5.0 |
| BF-6f7daf1 | fix: clone Windows Git Packages with core.longpaths so a deep file:// source path fits | PiG's own defect | Windows | Unreleased |
| BF-6fb1307 | openai-codex-responses reads instructions from the initial system message of the resolved transcript before… | PiG's own defect | All platforms | 0.5.0 |
| BF-70d51e5 | Fixed bash and PowerShell results losing Took after reloading a session, and the live Took including… | A fix Pi shipped that PiG ported | Windows | 0.5.0 |
| BF-70d51e5-2 | Fixed the message-format page: it said PiG does not carry the nestedCalls record on tool result messages… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-70d8a41 | Keybinding conflict detection ignores user bindings for actions the table does not define, as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-70e1841 | /settings warnings submenu: a warning the settings never set shows as on and a toggle always reports the… | PiG's own defect | All platforms | 0.5.0 |
| BF-7177d5f | fix(tui): card of an unregistered tool is Pi's formatToolExecution | PiG's own defect | All platforms | Unreleased |
| BF-72e7ab2 | A tool card whose app.tools.expand key is unbound shows an empty key in ".. | PiG's own defect | All platforms | 0.5.0 |
| BF-72e7ab2-2 | TestMcpManagerViewMatchesPi renders the /mcp manager footers under rebound, multi-key and unbound… | PiG's own defect | All platforms | 0.5.0 |
| BF-72e7ab2-3 | TestLlamaViewMatchesPi runs a quarter of its 700 scenarios under rebound, multi-key and unbound… | PiG's own defect | All platforms | 0.5.0 |
| BF-72e7ab2-4 | TestCodemodeCardsMatchPi and TestMcpToolCardsMatchPi repeat their collapsed probes under rebound… | PiG's own defect | All platforms | 0.5.0 |
| BF-72e7ab2-5 | TestBashExecutionComponentMatchesPi probes the collapsed expand hint under rebound, multi-key and unbound… | PiG's own defect | All platforms | 0.5.0 |
| BF-72e7ab2-6 | TestSummaryAndSkillMessageComponentsMatchPi probes the compaction, branch summary and skill messages under… | PiG's own defect | All platforms | 0.5.0 |
| BF-731474b | coding.DefineTool returns the tool definition it is given, as Pi's defineTool does | PiG's own defect | All platforms | 0.5.0 |
| BF-731474b-2 | codemode.CodemodeJsonSchema names the raw JSON of a tool's JSON Schema document, as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-7375fe4 | After pig piglet update installs a new release of a pulled Piglet Binary, pig piglet list shows the… | PiG's own defect | All platforms | 0.5.0 |
| BF-73a09eb | fix: the ANSI renderer that replaces a frontend surface draws on the options terminal, not the surface's… | PiG's own defect | All platforms | Unreleased |
| BF-73a6c8c | OpenAI-compatible chat completions requests keep the tool-call ids of the same model's earlier calls as they… | PiG's own defect | All platforms | 0.5.0 |
| BF-73ef1ea | Reduced context-limit request failures by estimating input at 3.5 characters per token instead of 4 when… | PiG's own defect | All platforms | 0.5.0 |
| BF-7472171 | An extension dialog (select, confirm, input, editor) that opens while another is shown now replaces… | PiG's own defect | All platforms | 0.5.0 |
| BF-7489735 | pig -e <directory> now loads a Node extension whose entry file is index.mjs and that has no… | PiG's own defect | All platforms | 0.5.0 |
| BF-74bc69b | fix(rpc): every Session replacement aborts the compactions of its batch, and an abort before the request… | PiG's own defect | All platforms | Unreleased |
| BF-74e801f | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-74f806c | fix(agent): end the tool flow at custom user messages after /tree navigation (#203) | PiG's own defect | All platforms | Unreleased |
| BF-74fb98c | Markdown list items lex their text line by line like Pi: an HTML block starts at any item line, and an item… | PiG's own defect | All platforms | 0.5.0 |
| BF-75426aa | The /login and /logout provider picker and the /tree selector show the terminal cursor in their search… | PiG's own defect | All platforms | 0.5.0 |
| BF-75426aa-2 | A Node extension's tool_call handler can edit event.input in place and the tool runs with the edit, as in… | PiG's own defect | All platforms | 0.5.0 |
| BF-756cac8 | A streaming run no longer hangs, rarely and mostly under load, when a response turn resumes just as the drain… | PiG's own defect | All platforms | 0.5.0 |
| BF-75c6ccb | fix(codingagent): context edits leave system messages unchanged | PiG's own defect | All platforms | Unreleased |
| BF-7633906 | On arm64, tool_search and searchTools() scores use the same natural logarithm as Pi running on Node for… | PiG's own defect | All platforms | 0.5.0 |
| BF-7662334 | An extension that writes a large amount to stderr no longer grows its diagnostic log without limit | PiG's own defect | All platforms | 0.5.0 |
| BF-773a868 | RPC mode no longer hangs when a Node extension command calls pi.sendMessage(..., { triggerTurn: true })… | PiG's own defect | All platforms | 0.5.0 |
| BF-773a868-2 | RPC mode no longer hangs when a Node extension command calls pi.appendEntry and then pi.setSessionName… | PiG's own defect | All platforms | 0.5.0 |
| BF-7768bea | fix(ai): tool-call arguments are written as JSON.stringify writes them, and keep a lone surrogate | PiG's own defect | All platforms | Unreleased |
| BF-779318a | OpenAI-compatible streams where one chunk carries reasoning and content (LiteLLM and similar proxies) now… | PiG's own defect | All platforms | 0.5.0 |
| BF-78c4c6c | pig piglet add now names the offending bundled path and where it came from (for example `Package herdr… | PiG's own defect | All platforms | 0.5.0 |
| BF-78f2d55 | pig mcp login --help described --timeout as only the wait for the browser; it limits the whole sign-in… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-79eadcf | Long sessions no longer slow down each streamed frame in the main screen: a frame redraws from the first line… | PiG's own defect | All platforms | 0.5.0 |
| BF-79f9a9e | fix(agent): convert a live partial with one clone and keep events at an unchanged publication (TU-T7 rest) | PiG's own defect | All platforms | Unreleased |
| BF-7a4a8e8 | fix duplicate jsstring import in tui/assistant_message_block.go | PiG's own defect | All platforms | Unreleased |
| BF-7b0f5d7 | A native extension provider's filterAllModels receives the models its getAllModels (or getModels)… | PiG's own defect | All platforms | 0.5.0 |
| BF-7b59fe2 | pig piglet build --format binary from a Pig source directory that is not a Git checkout (for example… | PiG's own defect | All platforms | 0.5.0 |
| BF-7b92429 | find and grep trim like JavaScript: FindTool trimmed each fd output line with strings.TrimSpace where… | PiG's own defect | All platforms | 0.5.0 |
| BF-7ba3211 | A resumed session no longer holds its whole file in memory | PiG's own defect | macOS | 0.5.0 |
| BF-7c59949 | fix(codingagent): session selector keeps Pi's out-of-range selected index | PiG's own defect | All platforms | Unreleased |
| BF-7cca5ee | A Node extension can default-import TypeBox (import Type from "typebox", and likewise typebox/value… | PiG's own defect | All platforms | 0.5.0 |
| BF-7d0621f | fix(extension): keep Pi's three-parameter EmitBeforeAgentStart (prompt, images, options); a custom run's… | PiG's own defect | All platforms | Unreleased |
| BF-7dc9b09 | -e <local Package> now contributes its prompts, skills and themes, loaded before the project, user and… | PiG's own defect | All platforms | 0.5.0 |
| BF-7dc9b09-2 | --prompt-template and --theme paths are now merged after the project, user and Package resources, as in… | PiG's own defect | All platforms | 0.5.0 |
| BF-7e09ec0 | fix(codingagent): the idle collection runs one forced GC cycle (D172) | PiG's own defect | All platforms | Unreleased |
| BF-7e1a05c | Resource resolution follows Pi's DefaultPackageManager.resolve in two cases: built-in extensions come after… | PiG's own defect | All platforms | 0.5.0 |
| BF-7ea21f3 | RPC output writes summed usage (a tool result's usage with nested model calls, a compaction summary's usage)… | PiG's own defect | All platforms | 0.5.0 |
| BF-7f3a71c | fix(durable): the reference Harness wraps a plain Models once too | PiG's own defect | All platforms | Unreleased |
| BF-7f441d7 | MCP OAuth URLs are WHATWG URLs, as in Pi | PiG's own defect | All platforms | 0.5.0 |
| BF-7fc4ef4 | fix(coding): shut down the extension host of a DefaultResourceLoader an extension constructed when its last… | PiG's own defect | All platforms | Unreleased |
| BF-8001cc5 | SettingsManager collection getters (getPackages, getExtensionPaths, getSkillPaths, getPromptTemplatePaths… | PiG's own defect | All platforms | 0.5.0 |
| BF-8001cc5-2 | SettingsManager.getCompactionSettings (+ getCompactionReserveTokens, getCompactionKeepRecentTokens)… | PiG's own defect | All platforms | 0.5.0 |
| BF-801cadd | fix(codingagent): session files decode invalid UTF-8 as Node does | PiG's own defect | All platforms | Unreleased |
| BF-8048c85 | Tool-call header argument preview no longer splits a character: truncateArg (tui/tool_execution.go) cut… | PiG's own defect | All platforms | 0.5.0 |
| BF-8070fcc | OpenAI Codex and Azure OpenAI requests parse their URL as fetch does again: an upper-case scheme, a space or… | PiG's own defect | All platforms | 0.5.0 |
| BF-8085dc0 | A legacy oauth.json or settings.json that starts with a byte order mark migrates to auth.json, as in Pi | PiG's own defect | All platforms | 0.5.0 |
| BF-80970da | The Go SDK page now says that leaving AgentDir empty in CreateAgentSessionServicesOptions makes a program… | PiG's own defect | All platforms | 0.5.0 |
| BF-80b912a | A failed install-receipt write during pig update or install, such as one rejected because the update CA… | PiG's own defect | Windows | Unreleased |
| BF-80bb522 | Compacting a history longer than the model's context window no longer fails with the provider's "prompt is… | PiG's own defect | All platforms | 0.5.0 |
| BF-80bb522-2 | Gemini 2.x models (Google Generative AI and Vertex) no longer reject a conversation in which a parallel tool… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-80bb522-3 | Amazon Bedrock Converse requests merge adjacent messages that have the same role | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-80c797d | Text that holds ill-formed UTF-8 is written with one U+FFFD for each ill-formed sequence, as Node decodes it… | PiG's own defect | All platforms | 0.5.0 |
| BF-81e584b | anthropic-messages payload now matches Pi for four cases found by the new live-Pi oracle… | PiG's own defect | All platforms | 0.5.0 |
| BF-81e584b-2 | Live-Pi oracle TestMistralMessagesPayloadMatchesPi (ai/mistral_messages_payload_oracle_test.go… | PiG's own defect | All platforms | 0.5.0 |
| BF-8241433 | A Mistral stream chunk with a member of an unexpected type is read as Pi's JavaScript reads it instead of… | PiG's own defect | All platforms | 0.5.0 |
| BF-82d1808 | Subprocess extensions no longer slow down each streamed token in long sessions: the state sent before each… | PiG's own defect | All platforms | 0.5.0 |
| BF-83d5488 | pig install <dir> of a local directory with no package manifest and no extensions/, skills/, prompts/… | PiG's own defect | All platforms | 0.5.0 |
| BF-83fea11 | On Windows, a refused connection reads as Node's connect ECONNREFUSED and is not retried, as in Pi; it was… | PiG's own defect | Windows | 0.5.0 |
| BF-844d215 | Repairing a partial tool-call arguments text follows partial-json's obj["__proto__"] = value: a… | PiG's own defect | All platforms | 0.5.0 |
| BF-848836a | fix(frontmatter): header starts one UTF-16 unit after the fence | PiG's own defect | All platforms | Unreleased |
| BF-852652e | PiG no longer names documentation that does not exist | PiG's own defect | All platforms | 0.5.0 |
| BF-852f029 | Durable document, conversation-view and task-graph states report failing state listeners to the Session's… | PiG's own defect | All platforms | 0.5.0 |
| BF-85618d9 | fix(rpc): a failed switch_session answers before the replacement ahead of it; prompt compaction checks… | PiG's own defect | All platforms | Unreleased |
| BF-85908ae | A /tree branch summary the provider refuses as too long is retried with the newest half of the request… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-85a7b01 | A prompt now fails with Pi's error when the Session's history holds a system message whose content is a… | PiG's own defect | All platforms | 0.5.0 |
| BF-85c98be | durable/interop/bench.mjs and sqlhost-bench seed\|sample report the process's own peak RSS on Linux… | PiG's own defect | Linux | 0.5.0 |
| BF-85ca0f5 | Rebuilding the transcript (resume, /tree, display settings) now places cache-miss notices as Pi does in a… | PiG's own defect | All platforms | 0.5.0 |
| BF-860776a | A Google provider built from a model id without catalog metadata now sends its own earlier thinking blocks as… | PiG's own defect | All platforms | 0.5.0 |
| BF-86364b4 | fix(minimatch): case-insensitive patterns compare as a JavaScript RegExp does | PiG's own defect | All platforms | Unreleased |
| BF-866c7f9 | fix(rpc): two prompts of one batch answer in request order | PiG's own defect | All platforms | Unreleased |
| BF-869cef8 | A pi-messages stream event that is a number, string, boolean or array is skipped, as Pi skips an event of… | PiG's own defect | All platforms | 0.5.0 |
| BF-86c411a | fix(builtin): codemode and tool_search declare their parameters in TypeBox key order | PiG's own defect | All platforms | Unreleased |
| BF-871b48d | pig install <source> --validate-only now validates git and npm sources that are not installed yet | PiG's own defect | All platforms | 0.5.3 |
| BF-87a6ac5 | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-88177e8 | fix(cli): type the validate-only scratch-fetch marker as D28 and extend the record | PiG's own defect | All platforms | Unreleased |
| BF-8894a13 | A double-quoted YAML string in a skill, prompt template or parseFrontmatter header now reads a… | PiG's own defect | All platforms | 0.5.0 |
| BF-8894a13-2 | The liveness conformance harness answers the host-parser helper calls a factory makes before the register… | PiG's own defect | All platforms | 0.5.0 |
| BF-893ff97 | A read call whose offset or limit arrives as a string shows the right line range (:25-37, not… | PiG's own defect | All platforms | 0.5.0 |
| BF-89d167f | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-8a18a6b | A Node extension no longer keeps reading an entry the host refused | PiG's own defect | All platforms | 0.5.2 |
| BF-8a25539 | llama-cpp-classify answers match Pi bit for bit: the softmax uses jsmath.Exp, V8's fdlibm Math.exp… | PiG's own defect | All platforms | 0.5.0 |
| BF-8a88c34 | On Windows, a custom theme edited in a rapid burst of writes no longer stays on a stale version | PiG's own defect | Windows | Unreleased |
| BF-8a9bb40 | fix(ai): Anthropic retry-delay message and Codex error body without an error member | PiG's own defect | All platforms | Unreleased |
| BF-8ab5a89 | A long session no longer holds each tool result twice | PiG's own defect | All platforms | 0.5.0 |
| BF-8ab86c8 | RPC get_state sent right after new_session, fork, clone, switch_session or export_html now… | PiG's own defect | All platforms | 0.5.0 |
| BF-8b75dc0 | Config selector (pig config): every key that reaches the search input re-filters and resets the selection… | PiG's own defect | All platforms | 0.5.0 |
| BF-8b8f379 | A pi-ai call made while a module loads is answered in every extension host: SDK sessions, the extension… | PiG's own defect | All platforms | 0.5.0 |
| BF-8b9e467 | PIG_PROFILE=metrics reports the exact cumulative allocation counts | PiG's own defect | All platforms | 0.5.0 |
| BF-8bc7c8f | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-8c3f5a1 | fix(tui): a finished block's hidden thinking run keeps no streaming memos | PiG's own defect | All platforms | Unreleased |
| BF-8c5a7a4 | The Go, Python and Rust extension SDKs' serializeConversation (SerializeConversation… | PiG's own defect | All platforms | 0.5.0 |
| BF-8c7bacf | delta.AssertValidOp checks path safety as Pi's assertValidOp does: it rejects a negative, fractional… | PiG's own defect | All platforms | 0.5.0 |
| BF-8c7bacf-2 | delta.ApplyImmutable sets a numeric path segment on an object as that key, as Pi does | PiG's own defect | All platforms | 0.5.0 |
| BF-8c7bacf-3 | PathError and UnsafePathError messages print paths as JSON.stringify does: <, >, &, U+2028 and… | PiG's own defect | All platforms | 0.5.0 |
| BF-8d008eb | fix(interactive): paint the requested first frame before the color wait returns | PiG's own defect | All platforms | Unreleased |
| BF-8d23cfc | Saving a setting keeps the key order of the existing settings.json, at the top level and inside a patched… | PiG's own defect | All platforms | 0.5.0 |
| BF-8eb1202 | An OpenAI Chat Completions chunk with a member of an unexpected type (a number for delta.content… | PiG's own defect | All platforms | 0.5.0 |
| BF-8f45099 | Compaction summaries, branch summaries and bug reports omit thinking blocks (Pi 15e7bdc1, Pi #9602): replayed… | PiG's own defect | All platforms | 0.5.0 |
| BF-8f88d43 | On Windows, an extension's exec no longer returns no output for a command that printed, when the machine is… | PiG's own defect | Windows | Unreleased |
| BF-905f3d0 | A trap of the codemode module on a Worker no longer loses the output the script printed before it: the host… | PiG's own defect | All platforms | 0.5.0 |
| BF-9088293 | The Azure OpenAI base URL is parsed and serialized with the WHATWG URL parser, as Pi does: hosts are… | PiG's own defect | All platforms | 0.5.0 |
| BF-909581e | The terminal no longer stays in mouse-tracking, focus-reporting, Kitty keyboard, bracketed-paste or… | A fix Pi shipped that PiG ported | All platforms | 0.5.2 |
| BF-909a2e2 | MCP OAuth metadata documents with a member whose name differs from a modeled one only in case (for example… | PiG's own defect | All platforms | 0.5.0 |
| BF-90c1f0f | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-9101bc8 | Bedrock model matching follows Pi: separator runs in a model id or display name become a single - (so "Opus… | PiG's own defect | All platforms | 0.5.0 |
| BF-918f21c | formatThousands (tui/compaction_summary.go, compaction-summary-message.ts:40… | PiG's own defect | All platforms | 0.5.0 |
| BF-919575a | A JSON syntax error reports what V8's JSON.parse reports in two cases it got wrong: a number or string… | PiG's own defect | All platforms | 0.5.0 |
| BF-91e912a | Two PiG versions that share a config root (two installed versions, or PiG beside a Piglet Binary) no longer… | PiG's own defect | All platforms | 0.5.0 |
| BF-91f9946 | OpenRouter image generation reads the chat completions body as Pi does, checked against the pinned Pi in a… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c | A listener that fails while agent_start is dispatched now ends the run as in Pi (failure messages, no… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-10 | Constrained-sampling errors quote tool and property names as Pi does (raw text between double quotes, not… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-11 | GitHub Copilot model-catalog fetches now report a failed response as Pi does (`<status> <reason>: <untrimmed… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-12 | GitHub Copilot login now reports failed HTTP responses as Pi does (<status> <reason>: <body>, not `HTTP… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-13 | GitHub Copilot, ChatGPT and OpenAI Codex logins report what Pi reports when a server's response cannot be… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-14 | GitHub Copilot login sends the browser to the verification URL exactly as Pi does (new URL(...).href: the… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-15 | Device-code login polling now does its time arithmetic in floating-point milliseconds as Pi does, so a NaN… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-16 | A durable directory reader now refuses a maxEntries above Number.MAX_SAFE_INTEGER with an invalid… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-17 | Durable entries keep a present empty edits list ("edits":[]) when they are appended, stored and read… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-18 | Durable: a JSONL store whose task or submission record carries a status outside the closed set (a damaged… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-19 | Fixed Editor.SetText keeping the previous scroll offset: as in Pi, replacing the text puts the view back at… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-2 | A stored models catalog whose contextWindow or maxTokens is written as 128000.0 or 1.28E5 loads like… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-20 | Fixed frontmatter parsing when the opening --- is followed by a multi-byte character (for example U+0085 or… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-21 | Google streams replace an ill-formed UTF-8 sequence with one U+FFFD per maximal subpart (the WHATWG… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-22 | Case-insensitive matching of tool names (Claude Code tool casing), the anthropic-beta, reserved Bedrock and… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-23 | Kimi Code OAuth login and refresh now read the server's response the way Pi does: a body that is not a JSON… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-24 | Kimi Code and xAI OAuth decode a response body as fetch does: ill-formed UTF-8 becomes one U+FFFD per maximal… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-25 | A tool or command that throws an Error with an empty message reports an empty message in the Node runtime… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-26 | The Rust SDK parses every JSON number as the nearest double, as JSON.parse does; it could land one unit in… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-27 | The Go SDK writes NaN and infinities as null (and negative zero as 0) in the frames it sends, as… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-28 | The Node SDK's messageRole now returns "" for a message whose role is not a string, as the Go, Rust and… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-29 | The Go and Rust SDKs now answer a tool result past the 128 MiB frame limit with a frame too large error, as… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-3 | Provider HTTP error bodies are decoded like response.text(): ill-formed UTF-8 becomes one U+FFFD per… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-30 | The Python SDK sends NaN and infinities as null (as JSON.stringify does) instead of the bare words… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-31 | The Rust SDK decodes tool arguments, notifications and host-call results nested past 128 levels (up to 4096)… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-32 | The Rust SDK no longer drops its host connection when a frame (a tool argument, event payload or object key)… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-33 | The login dialog renders its one input at every prompt position until a submit replaces them with the… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-34 | Markdown: a rule written with the list's own marker (* * *) after a blank line ends the list with its blank… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-35 | Markdown: a list item line that is a rule (- a, a blank line, - - -; or - * * *) renders as a rule… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-36 | Markdown: an indented line that a table delimiter row (---) follows joins the paragraph before it with its… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-37 | Markdown: a text that ends in one line of spaces after an indented code block, an HTML block or a table no… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-38 | Markdown no longer adds a blank row after a quote or block when only a repeated link definition follows it… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-39 | Markdown: a line that continues a nested list item's paragraph (- a, - b, x) renders at the nested… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-4 | A Gemini (or Bedrock/native) response streamed in many small chunks no longer hangs the stream… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-40 | Markdown no longer adds an empty row after a table that is too narrow to draw, when the table is inside a… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-41 | Markdown: list items whose markers are indented by different amounts (0 to 3 spaces) form one list, so a… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-42 | Markdown rendering shows a horizontal rule (- - -, * * *) inside a nested list item as a rule, as Pi… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-43 | Markdown nested lists treat a less-indented continuation line the way Pi does: a line indented to a nested… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-44 | Markdown: a blockquote whose last quoted line holds only spaces or tabs (> q, >, x) now renders the… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-45 | Markdown task lists whose later item is a heading (- [ ] b over ===) keep and strip checkbox markers on… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-46 | MCP HTTP servers and the OAuth requests of a sign-in now report a connection that fails before any response… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-47 | Meta OAuth login now reads the server's responses as Pi does: an invalid device-authorization response is… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-48 | Case-insensitive glob matching compares as a JavaScript regular expression does: İ no longer matches i… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-49 | OpenAI ChatGPT and Codex OAuth token failures with an empty body report the reason phrase the server sent… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-5 | Sign in with ChatGPT now reads the token endpoint's responses as Pi does: a failed response quotes the body… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-50 | OpenRouter OAuth key exchange now ignores a leading byte order mark in the response body, as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-51 | Fullscreen double-click selects the word Pi selects: words come from the same ICU segmentation as… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-52 | Anthropic: a server-requested retry delay above the cap fails with `Server requested Ns retry delay (max… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-53 | OpenAI Codex: an error body whose error member is absent or falsy (an object without it, an array, null)… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-54 | Provider streams read integers written as 12.0, 1.2e1 or 12E0 (a token count, an index) as JavaScript's… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-55 | Radius OAuth login now reads the gateway's responses as Pi does: an error body that is a JSON string, number… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-56 | Radius OAuth login again signs in when the gateway's token response has no refresh token, as Pi does | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-57 | A provider request whose URL path Go cannot parse no longer sends the URL fragment to the server; fetch never… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-58 | The Anthropic stream reader takes an event's type only from a member named exactly type, as Pi does; a… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-59 | A provider HTTP error body that starts with two byte order marks reads as response.text() reads it: both… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-6 | The codemode-store session entry lists the keys of its set object as Pi does (array-index keys ascending… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-60 | A models.json whose contextWindow, maxTokens or a model override writes an integer as 128000.0 or… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-61 | Classifier (typesafe-system-one, llama.cpp) and Radius gateway config responses are decoded as… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-62 | Amazon Bedrock streams read integers written as 12.0 or 1e0 (a content block index, a token count) as the… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-63 | An OpenAI Codex HTTP error with an empty body reports the reason phrase the server sent, or "Request failed"… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-64 | Print mode (-p) now prints the partial text, writes a warning to stderr and exits 1 when the final response… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-65 | Delayed terminal replies (OSC color, XTVersion, Kitty graphics) that arrive in pieces are no longer typed… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-66 | In the alternate screen, wheel input that a focused overlay does not handle scrolls the transcript (Pi 1.2.0… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-67 | A session file with invalid UTF-8 inside a record loads with one U+FFFD per maximal invalid subpart, as… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-68 | A system message's sections object now enumerates integer-like section names first, in ascending order… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-69 | A configured thinking budget of zero (or below) now wins over the default in the OpenAI-compatible, Google… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-7 | OpenAI Codex device-code login now reads the server's responses the way Pi does: a field of an unexpected… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-70 | A tool call whose arguments hold a key with an escaped lone surrogate ("\ud800a") keeps that key's place in… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-71 | The agent loop now gives a custom stream function Pi's messages (errored and aborted turns kept, no synthetic… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-72 | A real provider's tool-flow repair (D48) no longer moves a tool result back to an earlier unanswered call… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-73 | Fixed the session tree selector keeping its cursor inside the rows | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-74 | SettingsList draws Pi's bytes: its scroll indicator, description and hint rows are no longer padded to the… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-75 | A ! command block draws Pi's bytes: two consecutive blocks are separated by one blank row instead of two… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-76 | A provider defined only by virtual models reports configured with source environment and label virtual… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-77 | Provider order puts a provider of only virtual models where Pi's providerIds does (the place of its… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-78 | xAI OAuth login and refresh now read the server's response the way Pi does: a body that is not a JSON object… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-8 | OpenAI Codex and OpenRouter OAuth responses read ill-formed UTF-8 and a body that fails to read as fetch… | PiG's own defect | All platforms | 0.5.0 |
| BF-928e15c-9 | An OpenAI Codex login or refresh whose success response is not JSON reports JavaScript's own message, as Pi… | PiG's own defect | All platforms | 0.5.0 |
| BF-92da987 | Codemode no longer reserves its whole 256 MiB script-memory limit when a session starts: the QuickJS linear… | PiG's own defect | All platforms | 0.5.0 |
| BF-93319f4 | The terminal UI worked example now describes the typed /model prefix in words | PiG's own defect | All platforms | 0.5.0 |
| BF-93c09d6 | A local package source that is a file Node cannot import, such as a packed .tgz, fails to load as in Pi… | PiG's own defect | All platforms | 0.5.0 |
| BF-93c09d6-2 | pig install --validate-only on a Package root validates every extension the Package names in… | PiG's own defect | All platforms | 0.5.0 |
| BF-93c5def | A built-in extension named with -e builtin:<name> loads before the extensions from settings and the… | PiG's own defect | All platforms | 0.5.0 |
| BF-93f8b7c | fix(agent): a listener failure at agent_start ends the run as in Pi; failure messages carry the model's api | PiG's own defect | All platforms | Unreleased |
| BF-94e3498 | On Windows, PiG no longer searches C:\etc\pig\piglets or C:\opt\pig\piglet.yaml for a Piglet | PiG's own defect | Windows | 0.5.0 |
| BF-94e6796 | A Durable Harness task that went to sleep for a short time, such as the 1 ms backoff before a generation… | PiG's own defect | All platforms | 0.5.2 |
| BF-94f1e6f | fix(coding): a provider of only virtual models is configured with source virtual | PiG's own defect | All platforms | Unreleased |
| BF-9515bcb | Code mode: a deterministic run's clocks round like Math.round (0.49999999999999994 ms read 1 ms), and a… | PiG's own defect | All platforms | 0.5.0 |
| BF-958e80d | A tool call whose arguments hold a number out of float64 range (1e999, -1e999) no longer ends the session… | PiG's own defect | All platforms | Unreleased |
| BF-95a5f79 | RPC mode starts the --tools list active in its own order when it names extension tools, as Pi does | PiG's own defect | All platforms | 0.5.0 |
| BF-95eefe2 | openai-completions thinking formats now match Pi (ai/openai.go, openai-completions.ts:885-985 and… | PiG's own defect | All platforms | 0.5.0 |
| BF-9721c48 | An auto, manual or overflow compaction status shown in the editor's top border now takes the editor border… | PiG's own defect | All platforms | 0.5.0 |
| BF-9740507 | pig extension upgrade now rewrites a Context.ConfigHome() call in an if or switch init statement and… | PiG's own defect | All platforms | 0.5.0 |
| BF-9740507-2 | pig extension upgrade no longer discards the error of Context.ConfigHome with `home, _ :=… | PiG's own defect | All platforms | 0.5.0 |
| BF-9740507-3 | pig extension upgrade is all or nothing | PiG's own defect | All platforms | 0.5.0 |
| BF-976af87 | The documentation said PiG starts without a first-time setup screen and that Stock PiG has no built-in sprites | PiG's own defect | All platforms | 0.5.0 |
| BF-97cd675 | fix(agent): nested records keep a message whatever the spelling of its numbers | PiG's own defect | All platforms | Unreleased |
| BF-97e178f | Tool-call arguments that arrive as incomplete or malformed JSON are now recovered exactly as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-983d651 | The bash truncation notice for a last line over 50 KB that ends with a newline reports the line's size… | PiG's own defect | All platforms | 0.5.0 |
| BF-985b5f9 | fix(coding): a resumed session takes its active tools from the transcript (pi-mono #10785) [SIGNOFF-PENDING] | PiG's own defect | All platforms | Unreleased |
| BF-992f3cb | A before_provider_request replacement that is not an object leaves Anthropic Messages as Pi's `{ ...value… | PiG's own defect | All platforms | 0.5.0 |
| BF-99a06cf | Interactive startup paints the editor and footer first and adds the startup header once the terminal reports… | PiG's own defect | All platforms | 0.5.0 |
| BF-9af407c | fix(ai): race builds read provider sockets through syscall.Read | PiG's own defect | All platforms | Unreleased |
| BF-9b4312b | A Rust extension build that ends without a compiler diagnostic (cargo or rustc killed, cargo unable to start)… | PiG's own defect | All platforms | 0.5.0 |
| BF-9b4312b-2 | The Rust build failure test asserts that a killed rustc is named by its program name, not its full path | PiG's own defect | All platforms | 0.5.0 |
| BF-9b63b2b | On Windows, the bash tool no longer risks waiting forever after a command exits while a process it started in… | PiG's own defect | Windows | 0.5.0 |
| BF-9b63b2b-2 | A running ! command or bash tool command no longer outlives pig when the session ends because its… | PiG's own defect | All platforms | 0.5.0 |
| BF-9ba2589 | fix(agent): write a decoded message as the object it was decoded from | PiG's own defect | All platforms | Unreleased |
| BF-9be5765 | Pressing Escape while an extension tool runs now lets the tool finish: a tool that stops on its abort signal… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-9c1a0e9 | An OpenAI Responses stream event or output item with a member of an unexpected type is read as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-9c98038 | An unclosed $$ or \[ display-math block no longer swallows or corrupts every Markdown table after it | A fix Pi shipped that PiG ported | All platforms | 0.5.2 |
| BF-9d4fe9c | Selecting a model or cycling models with Ctrl+P no longer freezes the interactive mode when an extension's… | PiG's own defect | All platforms | 0.5.0 |
| BF-9d64980 | fix(coding): provider order and availability of virtual models follow Pi | PiG's own defect | All platforms | Unreleased |
| BF-9f4702c | A failed extension shortcut handler is now reported as Error: Shortcut handler error: … in the error… | PiG's own defect | All platforms | 0.5.0 |
| BF-D110 | Git sources: #subdirectory= selects a subdirectory; git:// and upper-case schemes yield usable… | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D150 | --no-stdin skips the startup read of piped stdin (Pi #10415) | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D151 | grep with context reads a window of each matched file (Pi #9276) | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D153 | The bash truncation notice names the size of the last line, not the bytes after the final newline (Pi #10015) | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D155 | Selection lists page with PageUp and PageDown (Pi #7629) | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D156 | MCP server url expands environment references (Pi #10654) | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D195 | An empty AWS_BEARER_TOKEN_BEDROCK is no token, where Pi's AWS SDK treats the variable as set and the request… | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D196 | Under skip-auth a bearer token from the process environment is not sent over an http:// endpoint, where Pi's… | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D198 | The Go, Rust and Python Type has no Codec family (Codec, Decode, Encode, DecodeBuilder… | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D200 | With no home directory PiG still runs the commands that load no resources, where Pi crashes at import, and… | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-D203 | registerProvider with an empty apiKey leaves the provider unconfigured where Pi treats the empty string as… | A Pi defect that PiG fixed first | All platforms | Unreleased |
| BF-a076a50 | A model request on a reused connection gets the full HTTP idle timeout to receive its first response bytes… | PiG's own defect | All platforms | 0.5.0 |
| BF-a09da26 | pi.getSettings() in a subprocess extension lists the settings in Pi's order: the global settings file's… | PiG's own defect | All platforms | 0.5.0 |
| BF-a09da26-2 | A bash or PowerShell tool card reads the result's truncation details by their exact keys, as Pi does: a… | PiG's own defect | Windows | 0.5.0 |
| BF-a110f68 | fix(tui,codingagent): owner-loop frame deadline and reader-side input routing (TU-T11, TU-T12) | PiG's own defect | All platforms | Unreleased |
| BF-a13bae4 | A read result shown in the transcript no longer keeps the file text a second time as its structured content | PiG's own defect | All platforms | 0.5.0 |
| BF-a156ef3 | On Linux, clipboard reads accept every DISPLAY spelling Pi's xcb_connect accepts: white space or a sign… | PiG's own defect | Linux | 0.5.0 |
| BF-a178939 | The Go and Python serializeConversation keep an unpaired UTF-16 surrogate in a message, a tool-call name or… | PiG's own defect | All platforms | 0.5.0 |
| BF-a1fa6d1 | NO_PROXY entries and ports are trimmed with JavaScript's whitespace rules, as in Pi: an entry or port that… | PiG's own defect | All platforms | 0.5.0 |
| BF-a238095 | coding: fix session executor queue-prefix race and durable execMu waits | PiG's own defect | All platforms | Unreleased |
| BF-a3859aa | An OpenAI-compatible provider that streams a tool call without an id or a function name (OpenRouter and GLM… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-a3bfab2 | A Radius gateway config cached on an OAuth credential is sanitized as Pi does | PiG's own defect | All platforms | 0.5.0 |
| BF-a40a16d | fix(codingagent): typed input restarts the idle collection's wait on every route | PiG's own defect | All platforms | Unreleased |
| BF-a46f930 | A Package configured as a settings object reads a resource kind its pi manifest does not declare (or… | PiG's own defect | All platforms | 0.5.0 |
| BF-a4753ec | A panic in a background goroutine of an interactive session no longer ends PiG with only a stderr dump | PiG's own defect | All platforms | 0.5.0 |
| BF-a4753ec-2 | The prompt-cache refresh that fires minutes after a prompt now treats a panic in the extension decision or in… | PiG's own defect | All platforms | 0.5.0 |
| BF-a4753ec-3 | When the terminal's input ends while its output is still there, PiG keeps running as Pi does instead of… | PiG's own defect | All platforms | 0.5.0 |
| BF-a47eb1a | A tool result from a Rust or Python extension can set the collapsed-view preview the Go SDK already had… | PiG's own defect | All platforms | 0.5.0 |
| BF-a53f72f | A context an extension kept across a session replacement or reload now raises Pi's stale message from cwd… | PiG's own defect | All platforms | 0.5.0 |
| BF-a5524c8 | In print and JSON mode, an extension whose session_shutdown handler throws after SIGTERM or SIGHUP now has… | PiG's own defect | All platforms | 0.5.0 |
| BF-a5524c8-2 | In print and JSON mode, SIGTERM or SIGHUP during a tool call stops the run where Pi does while the… | PiG's own defect | All platforms | 0.5.0 |
| BF-a690122 | PIG_PROFILE=heap and the heap_live_bytes of PIG_PROFILE=metrics count only memory PiG retains | PiG's own defect | All platforms | 0.5.0 |
| BF-a6932b4 | fix(coding): pass a Node extension request's client identity to the provider | PiG's own defect | All platforms | Unreleased |
| BF-a6e0ce8 | fix(codingagent): session selector page keys keep the index valid (Pi #7680) | PiG's own defect | All platforms | Unreleased |
| BF-a739b6d | The Bedrock endpoint region is read from the WHATWG hostname of the base URL, as Pi's new URL does… | PiG's own defect | All platforms | 0.5.0 |
| BF-a7be906 | Pressing Enter in the model picker's search box selects the highlighted model even when tui.select.confirm… | PiG's own defect | All platforms | 0.5.0 |
| BF-a7d2ef5 | Path autocomplete after resuming a session created in another directory now lists files from that session's… | PiG's own defect | All platforms | 0.5.0 |
| BF-a81ec0d | A key pressed while an extension's custom overlay is open no longer freezes pig when the key reaches the… | PiG's own defect | All platforms | 0.5.0 |
| BF-a8a04f4 | PIG_DEBUG writes its log with mode 0600 and refuses a symlink or a file owned by another user in the shared… | PiG's own defect | All platforms | 0.5.0 |
| BF-a8ff0c7 | The anthropic-messages and google-generative-ai requests build their URL as @anthropic-ai/sdk and… | PiG's own defect | All platforms | 0.5.0 |
| BF-a901509 | OpenRouter turns (the built-in provider and custom providers on OpenRouter's endpoint) now take OpenRouter's… | PiG's own defect | All platforms | 0.5.3 |
| BF-a94455c | The durable code mode joins the text items of a script's output in linear time (Pi f11e50f7… | PiG's own defect | All platforms | 0.5.0 |
| BF-a9757c8 | A Cloudflare model built with coding.BuildModel and streamed through its own Provider resolves… | PiG's own defect | All platforms | 0.5.0 |
| BF-aa2003c | The edit tool's prepared arguments keep the members in the order the model sent them | PiG's own defect | All platforms | 0.5.0 |
| BF-aa69959 | Key matching follows Pi for more terminal inputs and key identifiers | PiG's own defect | All platforms | 0.5.0 |
| BF-aabd300 | An OpenAI Responses, Azure OpenAI Responses or Google stream event whose JSON is a number, string, boolean or… | PiG's own defect | All platforms | 0.5.0 |
| BF-ab0ee71 | Hiding an overlay whose preFocus is another overlay, such as a panel focused when the overlay was shown over… | PiG's own defect | All platforms | 0.5.0 |
| BF-ab5be97 | Long streamed answers render each frame faster: the Markdown view re-wraps and re-renders only the lines and… | PiG's own defect | All platforms | 0.5.0 |
| BF-ac0dc3b | An empty list item ends its list: a following unindented line is no longer taken for its lazy continuation… | PiG's own defect | All platforms | 0.5.0 |
| BF-ac6fcea | A session_start handler that calls ctx.shutdown() during /reload ends the session and exits 0, as Pi… | PiG's own defect | All platforms | 0.5.0 |
| BF-aca35e7 | In print and JSON mode, a Node extension no longer runs a timer or other callback between a prompt's last… | PiG's own defect | All platforms | 0.5.0 |
| BF-ad49f9f | pig status no longer reports a shared extension name as an unhealthy duplicate | PiG's own defect | All platforms | Unreleased |
| BF-ad9ed64 | Appending a tool result whose details are a tool's own value (a truncated read, bash output details, an… | PiG's own defect | All platforms | 0.5.0 |
| BF-ad9ed64-2 | A large message's session record no longer keeps a regrown buffer when its text has escapes such as newlines… | PiG's own defect | All platforms | 0.5.0 |
| BF-aeaaf97 | Model requests reuse their HTTP connection | PiG's own defect | All platforms | 0.5.0 |
| BF-af1fe1a | Cycling models in the interactive UI goes through the Session as in Pi: the new model persists as the… | PiG's own defect | All platforms | 0.5.0 |
| BF-af36fe8 | A streamed tool call whose arguments are valid JSON but not an object ([], null, 5, "s") keeps them… | PiG's own defect | All platforms | 0.5.0 |
| BF-afa1dd5 | Amazon Bedrock requestMetadata keeps its keys' insertion order on the wire, as Pi's AWS SDK writes the… | PiG's own defect | All platforms | 0.5.0 |
| BF-afa1dd5-2 | make port-lint has three more checks: regexpinfunc (a constant pattern compiled inside a function)… | PiG's own defect | All platforms | 0.5.0 |
| BF-affc944 | Bedrock requests that fail with The pending stream has been canceled after a stalled HTTP/2 connection are… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-b02876b | chord/host: a link that is already closed when a host accepts or routes to it is no longer kept as a… | PiG's own defect | All platforms | 0.5.0 |
| BF-b0608a9 | OpenRouter generated-image data URLs are matched exactly as Pi does (^data:([^;]+);base64,(.+)$): a URL… | PiG's own defect | All platforms | 0.5.0 |
| BF-b06ad92 | An MCP server url can use environment variables ("url": "${TOOLS_URL}"): PiG checks and connects to the… | PiG's own defect | All platforms | 0.5.0 |
| BF-b18a7ff | ctx.compact answers onComplete with the compaction result's members in Pi's order (summary… | PiG's own defect | All platforms | 0.5.0 |
| BF-b2b0c63 | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-b2c219f | fix(runtimecell): satisfy the divergence, port and perf gates for the usage-lease and fingerprint changes | PiG's own defect | All platforms | Unreleased |
| BF-b341a82 | go install github.com/MichaelKinsy/PiG/cmd/pig@latest works again | PiG's own defect | All platforms | 0.5.2 |
| BF-b3543f8 | Fixed MCP stdio servers staying alive after PiG exited because the terminal closed, it crashed, or it… | PiG's own defect | All platforms | 0.5.0 |
| BF-b430c7a | Extension widgets now appear in the order they were set when Node or other extension-process widgets and Go… | PiG's own defect | All platforms | 0.5.0 |
| BF-b4504dd | Amazon Bedrock request bodies match Pi's: PiG writes the ConverseStream body itself in the member order Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-b47b6bc | fix(tui): number the editor wrap divergence D159 (lead block), cite Pi's measured RangeError; add… | PiG's own defect | All platforms | Unreleased |
| BF-b4822ef | pig install <dir> now installs a local Go, Rust or Python extension folder and loads it as an extension, as… | PiG's own defect | All platforms | 0.5.0 |
| BF-b4c6569 | ~/dir/ expands to the home directory with the trailing separator kept ($HOME/dir/), as Node's path.join… | PiG's own defect | All platforms | 0.5.0 |
| BF-b4e83db | Anthropic tools list eager_input_streaming and strict before input_schema, and the input schema and… | PiG's own defect | All platforms | 0.5.0 |
| BF-b4f1d4a | Pasting a large text that arrives in many terminal reads no longer stalls input: the paste is joined once… | PiG's own defect | All platforms | 0.5.0 |
| BF-b54f9aa | The tree selector label time of a session entry whose timestamp is not a valid date prints "aN/NaN/NaN… | PiG's own defect | All platforms | 0.5.0 |
| BF-b5b7b43 | Frontmatter YAML parsing matches the yaml library on text outside ASCII and on unterminated quotes: an… | PiG's own defect | All platforms | 0.5.0 |
| BF-b5ebeb5 | A resumed session draws its ! and !! command blocks, as Pi does | PiG's own defect | All platforms | 0.5.0 |
| BF-b5fb4aa | fix(tui): a streamed Markdown is not resumed past a paragraph that a later LaTeX closer clips; keep wraps… | PiG's own defect | All platforms | Unreleased |
| BF-b607c35 | Started from a directory reached through a symlink (a $PWD naming a link), pig now uses the physical… | PiG's own defect | All platforms | 0.5.0 |
| BF-b684b81 | RPC and session JSON write an assistant message's members in the order Pi's providers assign them… | PiG's own defect | All platforms | 0.5.0 |
| BF-b6c65b3 | ! command and RPC bash output no longer keeps fragments of color codes, such as a stray m, when an… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-b6d8ab0 | Fix changelog fragment and bug ledger naming the pre-squash commit of #307 (#317) | PiG's own defect | All platforms | Unreleased |
| BF-b730205 | OpenRouter image models that only the Images API serves (for example openai/gpt-image-2.5-flare) now… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-b751326 | When an extension in a shared Go, Rust or Python process crashes, the error for each stopped extension now… | PiG's own defect | All platforms | 0.5.0 |
| BF-b78cdae | A YAML parse error on a block scalar used as an implicit key (name: x followed by \|description: ...)… | PiG's own defect | All platforms | 0.5.0 |
| BF-b84facb | fix(codingagent,tui): extension editor follows the terminal rows; title and image fallback close only the… | PiG's own defect | All platforms | Unreleased |
| BF-b88044f | A login flow's text prompt accepts an empty answer, as Pi's does, instead of failing with "<prompt> is… | PiG's own defect | All platforms | 0.5.0 |
| BF-b8c5de6 | Interactive startup no longer reads terminal input while it checks for fd and rg unless one of them is being… | PiG's own defect | All platforms | 0.5.0 |
| BF-b8df7eb | On Windows, a session's private snapshot file is created in the temporary directory instead of the session… | PiG's own defect | Windows | 0.5.0 |
| BF-b8df7eb-2 | On Windows, an image fallback links a path that starts with a slash but no drive (/tmp/x.png) as Pi does… | PiG's own defect | Windows | 0.5.0 |
| BF-b8df7eb-3 | Alt-screen frames outside the Kitty protocol no longer read the environment for WezTerm on every render… | PiG's own defect | Windows | 0.5.0 |
| BF-b99457f | tool_search and codemode tool search match Chinese, Japanese and Korean queries: each such character is a… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-b9db06c | fix(subprocess): keep a user_bash operations object alive while its exec runs | PiG's own defect | All platforms | Unreleased |
| BF-ba8b6b7 | /logout reads stored credentials with Pi's 15-second signal, so an unreadable auth.json reports "Could… | PiG's own defect | All platforms | 0.5.0 |
| BF-ba9c37b | Starting pig no longer waits for the daily cleanup of the extension cache | PiG's own defect | All platforms | 0.5.0 |
| BF-bada2af | Session and crash timestamps are read like Date.parse of a JavaScript string: U+00A0 and U+FEFF between… | PiG's own defect | All platforms | 0.5.0 |
| BF-bb324f7 | A JavaScript or TypeScript extension loads when its registerCommand options carry a member Pi does not… | PiG's own defect | All platforms | 0.5.0 |
| BF-bb3d661 | An extension served in-process now refuses to serve a second connection while it serves one, with the error… | PiG's own defect | All platforms | Unreleased |
| BF-bb888ae | The /model picker now keeps its model list when the catalog refresh times out, as in Pi | PiG's own defect | All platforms | 0.5.0 |
| BF-bb9e9d8 | fix(extensions): parallel Node runtime linking reports the walk-order first failure under every schedule | PiG's own defect | All platforms | Unreleased |
| BF-bc24321 | fix(mem M3): arm the idle collection at run end and run it after 500 ms | PiG's own defect | All platforms | Unreleased |
| BF-bc4cd62 | toPrecision rounds a value just below a power of ten (1e-11 to 16 digits, 9.999999999999998e9 to 16 digits)… | PiG's own defect | All platforms | 0.5.0 |
| BF-bc8d37f | FileModelsStore roots and index keys: new live-Pi oracle… | PiG's own defect | All platforms | 0.5.0 |
| BF-bcc2046 | A nested list whose last item is empty but for spaces after its marker is followed by an empty row before the… | PiG's own defect | All platforms | 0.5.0 |
| BF-bd3e8ab | On arm64 and other CPUs with a fused multiply-add, Kimi, Radius and xAI OAuth credentials store the expires… | PiG's own defect | All platforms | 0.5.0 |
| BF-bd3e8ab-2 | On arm64 and other CPUs with a fused multiply-add, the cache-warming expected savings (shown by /session… | PiG's own defect | All platforms | 0.5.0 |
| BF-bdb7ad8 | A single server-sent-event line larger than 32 MiB (for example a base64 image chunk) no longer fails the… | PiG's own defect | All platforms | 0.5.0 |
| BF-beeb36a | Running with no home directory now fails the way Pi's package manager does, instead of reading the project's… | PiG's own defect | All platforms | Unreleased |
| BF-bf45320 | Go embedding hosts can bind extension appends through coding.Session.AppendExtensionEntry, retaining… | PiG's own defect | All platforms | Unreleased |
| BF-c1055c4 | Package manifest globs match as Node's fs.globSync does in Pi: brace sets and sequences, extglobs such as… | PiG's own defect | Windows, macOS | 0.5.0 |
| BF-c10ae73 | In RPC mode, runs that an extension command's handler sends with sendMessage or sendUserMessage start in… | PiG's own defect | All platforms | 0.5.0 |
| BF-c122088 | A durable nested call keeps its arguments in the member order the caller wrote them, as Pi does: the stored… | PiG's own defect | All platforms | 0.5.0 |
| BF-c1ed918 | fix(sdk-rs): give the request-start wire test's RequestMsg the batch, child_session, tool_call_index and… | PiG's own defect | All platforms | Unreleased |
| BF-c1fedc2 | The durable read tool reads a file that grows while it is read, such as an active log, instead of failing… | PiG's own defect | All platforms | 0.5.0 |
| BF-c1fedc2-2 | Bounded durable tool output drops a byte-order mark only at the start of byte output, and keeps a U+FEFF at… | PiG's own defect | All platforms | 0.5.0 |
| BF-c225c97 | fix(rpc): compaction_start precedes a refused replacement's error; D148 names the extra compaction_end; D141… | PiG's own defect | All platforms | Unreleased |
| BF-c229e1c | A Pi Durable SQLite file written by PiG can be read by Pi, and the reverse, when a task kind, request ID, or… | PiG's own defect | All platforms | 0.5.0 |
| BF-c2b2b8b | fix(tui): the inline memo is used only with tui's own style functions | PiG's own defect | All platforms | Unreleased |
| BF-c350117 | Fixed incorrect MCP reconnect behavior after cancellation during transport cleanup | PiG's own defect | All platforms | Unreleased |
| BF-c3a6eca | Markdown rendering follows Pi for lines made only of Unicode spaces (U+00A0, U+3000, U+2003 and the others… | PiG's own defect | All platforms | 0.5.0 |
| BF-c416945 | fix(ai): tool-argument member order keeps a key with an escaped lone surrogate | PiG's own defect | All platforms | Unreleased |
| BF-c47bafd | A fused Go extension's built-in tool execute without an extension context (`createBashTool(cwd).execute(id… | PiG's own defect | All platforms | 0.5.3 |
| BF-c4ad739 | Extension shortcut conflict warnings name both extensions ("registered by both A and B | PiG's own defect | All platforms | 0.5.0 |
| BF-c4d51d5 | The OpenAI Codex login reads the account id from the access token the way Pi does: the token payload is… | PiG's own defect | All platforms | 0.5.0 |
| BF-c4fae28 | Provider request bodies write a lone UTF-16 surrogate as its escape, as JSON.stringify does, instead of… | PiG's own defect | All platforms | 0.5.0 |
| BF-c4fef6c | Streaming long code blocks no longer grows memory with every frame: the syntax-highlight cache keeps one copy… | PiG's own defect | All platforms | 0.5.0 |
| BF-c51c77e | An extension that calls registerTool({}) or registerTool(null) now fails to load with Pi's error text… | PiG's own defect | All platforms | 0.5.0 |
| BF-c58ceed | chord/host.Forbidden("") keeps the empty message, as Pi's forbidden("") does; Forbidden() still says… | PiG's own defect | All platforms | 0.5.0 |
| BF-c5ce588 | Gemini 4 and later models send thinkingLevel instead of thinkingBudget, fixing Gemini 4 Argon on Vertex… | PiG's own defect | All platforms | 0.5.0 |
| BF-c625bb4 | The durable powershell tool fails with "No command to run" when its program list is empty, instead of… | PiG's own defect | Windows | 0.5.0 |
| BF-c6c2cf3 | An env request whose context is already cancelled is now always sent to the daemon and then cancelled, as… | PiG's own defect | All platforms | 0.5.0 |
| BF-c7ebb23 | A cancelled prompt in a config.oauth login now rejects with Pi's Login cancelled in every SDK (it was… | PiG's own defect | All platforms | 0.5.0 |
| BF-c7ebb23-2 | A config.oauth login's onManualCodeInput asks "Paste the authorization code", as Pi does, and a provider… | PiG's own defect | All platforms | 0.5.0 |
| BF-c8a90a6 | Pressing the login dialog's cancel key again cancels again, and Enter after a cancelled prompt leaves the… | PiG's own defect | All platforms | 0.5.0 |
| BF-c8e3d86 | Settings submenus: the type-to-filter box no longer shows a hardware cursor marker (Pi never focuses it), and… | PiG's own defect | All platforms | 0.5.0 |
| BF-c98e9f9 | Without HOME set, the project trust check takes the home directory from the user database, as Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-c9acbea | A Session listener that queues more messages than the Session's event buffer holds (64 by default), while it… | PiG's own defect | All platforms | 0.5.0 |
| BF-c9acbea-2 | A goroutine that waits to run Session events, such as the Session's event goroutine while a producer runs a… | PiG's own defect | All platforms | 0.5.0 |
| BF-c9f1cff | The experimental client takes Pi's ByteTransportFactory shape: ClientOptions.TransportFactory returns a… | PiG's own defect | All platforms | 0.5.0 |
| BF-c9fbfcc | Inline images render in Herdr again: TERM_PROGRAM=herdr detects the Kitty graphics protocol, as in Pi 1.2.0 | PiG's own defect | All platforms | 0.5.0 |
| BF-ca06a0b | Tool arguments that TypeBox's Convert wraps in an array (an object where the schema asks for an array, such… | PiG's own defect | All platforms | 0.5.0 |
| BF-ca27ba6 | The Go extension SDK accepts a native Provider object with no auth methods, as Pi's ProviderAuth (both… | PiG's own defect | All platforms | 0.5.0 |
| BF-ca675e0 | A list item whose text is [ ] or [x] with nothing after the spaces on the line is plain text, not a… | PiG's own defect | All platforms | 0.5.0 |
| BF-cb00273 | OpenRouter's signature-only reasoning.text delta for Claude is kept and replayed with its reasoning block… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-cb21ee0 | Markdown resolves link reference definitions as Pi does: [text][label], [label][] and [label] link to a… | PiG's own defect | All platforms | 0.5.0 |
| BF-cc9370b | A Node extension's setWidget, setHeader and setFooter factories receive the same tui as a… | PiG's own defect | All platforms | 0.5.0 |
| BF-ccaade2 | The /thinking selector edits its search like Pi's input, selects with the input's submit key, and no longer… | PiG's own defect | All platforms | 0.5.0 |
| BF-cd7fa1c | A read-only config root no longer prints warnings at every start | PiG's own defect | All platforms | 0.5.0 |
| BF-cda6e3a | PiG opens a session file that holds a valid JSON line that is not an entry (an array, true, a string) or an… | PiG's own defect | All platforms | 0.5.0 |
| BF-cdf0be7 | Markdown rendering matches Pi on blank lines at the start and end of the text, empty headings and closing #… | PiG's own defect | All platforms | 0.5.0 |
| BF-cdf2887 | fix(ai): keep a reused-id tool result in its own turn during the D48 repair | PiG's own defect | All platforms | Unreleased |
| BF-ce0f352 | createPowerShellTool now uses the operations option | A fix Pi shipped that PiG ported | Windows | 0.5.0 |
| BF-ce0f352-2 | The editor's autocomplete list takes its colors from one getSelectListTheme equivalent, the same function… | PiG's own defect | All platforms | 0.5.0 |
| BF-ce366dd | fix(coding): resume restores the transcript's tools only for the unchanged default selection… | PiG's own defect | All platforms | Unreleased |
| BF-ce42877 | durable/images.CreatePhotonImages, and durable/images/node.CreateNodePhotonImages with Worker: false… | PiG's own defect | All platforms | 0.5.0 |
| BF-ce8587e | On macOS, reading an image or file paths from the clipboard and writing text to it no longer risk passing… | PiG's own defect | macOS | 0.5.0 |
| BF-ced3a9c | RPC model objects of built-in models carry "type":"chat" (get_available_models, get_state, set_model… | PiG's own defect | All platforms | 0.5.0 |
| BF-ced3a9c-2 | A URL of a non-special scheme keeps an explicit port 0 (ssh://git@host:0/repo, an MCP OAuth redirect such… | PiG's own defect | All platforms | 0.5.0 |
| BF-cf4fff8 | A project whose only configuration is a project mcp.json (.pig/mcp.json, or .pi/mcp.json with… | PiG's own defect | All platforms | 0.5.0 |
| BF-cf648c7 | A restarted extension, isolated or in a recovered packed cell, now offers the commands, flags, shortcuts and… | PiG's own defect | All platforms | 0.5.0 |
| BF-cf7f368 | A local file listed as a Package with autoload: false still loads as one enabled extension, and when a… | PiG's own defect | All platforms | 0.5.0 |
| BF-d0c2727 | fix(codingagent): decode a Node request's client identity from its fields; regenerate the normalization… | PiG's own defect | All platforms | Unreleased |
| BF-d11ca55 | pig piglet build --format binary from a Pig checkout with a go.work builds a Piglet whose fused Go… | PiG's own defect | All platforms | 0.5.0 |
| BF-d133e3d | Codemode scripts that patch built-ins, for example Array.prototype.toJSON = ..., no longer break the tool… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-d133e3d-2 | MCP OAuth sign-in no longer fails with invalid_redirect_uri on OpenID Connect servers: client registration… | A fix Pi shipped that PiG ported | All platforms | 0.5.0 |
| BF-d1cbde3 | The pi-messages, Cloudflare Workers AI and TypeSafe System One, and OpenAI decisions request URLs are built… | PiG's own defect | All platforms | 0.5.0 |
| BF-d1f258f | The /settings search edits like Pi's search input: the cursor moves with the arrow keys, Ctrl+W, Ctrl+K… | PiG's own defect | All platforms | 0.5.0 |
| BF-d212cdf | pig --piglet piglets/standard/pig-standard.yaml no longer warns on every start that its piglogin… | PiG's own defect | All platforms | 0.5.0 |
| BF-d249c70 | SDK: Session.Dispose now releases the Session as Pi's dispose does | PiG's own defect | All platforms | 0.5.0 |
| BF-d26d494 | fix(tui): Editor.SetText resets the scroll offset | PiG's own defect | All platforms | Unreleased |
| BF-d276c00 | pig piglet schema lists the apis strip list | PiG's own defect | All platforms | 0.5.0 |
| BF-d2b1fc1 | GitHub Copilot login reads an enterprise domain the way Pi does: an IPv6 host keeps its brackets, an… | PiG's own defect | All platforms | 0.5.0 |
| BF-d2b1fc1-2 | Pasting an OAuth authorization into the Anthropic, OpenAI Codex or OpenRouter login now reads it the way Pi… | PiG's own defect | All platforms | 0.5.0 |
| BF-d2d4384 | A streamed write card no longer slows down as its content grows | PiG's own defect | All platforms | 0.5.0 |
| BF-d305b76 | A Go extension whose go.mod replace (or a Rust extension whose pig-sdk path dependency) points at an SDK… | PiG's own defect | All platforms | 0.5.0 |
| BF-d36b329 | The edit tool no longer accepts a JSON-string edits value that starts or ends with non-JSON whitespace such… | PiG's own defect | All platforms | 0.5.0 |
| BF-d37c769 | A replacement request body from a before_provider_request hook on Anthropic Messages, and every OpenAI… | PiG's own defect | All platforms | 0.5.0 |
| BF-d39069d | Tool cards rebuilt when a session is resumed now follow the showImages and imageWidthCells settings | PiG's own defect | All platforms | 0.5.0 |
| BF-d3b3618 | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-d42aebb | ai.ValidateToolArguments and ai.ValidateToolCall list a failed validation's errors in the order the… | PiG's own defect | All platforms | 0.5.0 |
| BF-d449f74 | tool_search and the Durable searchTools() ranker compute the same BM25 scores as Pi on arm64: the length… | PiG's own defect | All platforms | 0.5.0 |
| BF-d490a8b | Tool argument validation errors follow Pi: at most eight errors are listed (TypeBox's maxErrors), and… | PiG's own defect | All platforms | 0.5.0 |
| BF-d4ddb14 | The /scoped-models search input stays editable after a submit key reaches it (LF under the Kitty keyboard… | PiG's own defect | All platforms | 0.5.0 |
| BF-d50ec25 | The TypeScript extension runtime no longer depends on Node-only behaviour: its entry modules do not await the… | PiG's own defect | All platforms | 0.5.0 |
| BF-d514b3d | In a Node extension's before_agent_start handler, event.systemPrompt and ctx.getSystemPrompt() now… | PiG's own defect | All platforms | 0.5.0 |
| BF-d53cd09 | A tool call whose arguments are not an object is written back to each provider as Pi writes it: Anthropic and… | PiG's own defect | All platforms | 0.5.0 |
| BF-d60d422 | pi.exec gives the command PiG's environment unchanged, as Pi does, so a command started in a symlinked… | PiG's own defect | All platforms | 0.5.0 |
| BF-d64f8ed | durable.StorageRequestError and durable.SessionFailed are Pi's error classes of the same names… | PiG's own defect | All platforms | 0.5.0 |
| BF-d6aaf72 | A setTimeout(0) or setImmediate callback that an extension queues while an RPC stdin command runs (a… | PiG's own defect | All platforms | 0.5.2 |
| BF-d6e0dca | The extension SDK surface matrix and the extension API pages say that setEditorComponent and… | PiG's own defect | All platforms | 0.5.0 |
| BF-d7855fd | A subprocess extension's message, entry or tool renderer, or tool renderer resolver, that was busy past the… | PiG's own defect | All platforms | 0.5.0 |
| BF-d7bfea6 | A git source whose ref has no repository (git:#v1) no longer panics, and a ref split out of an opaque-path… | PiG's own defect | All platforms | 0.5.0 |
| BF-d7c7f86 | fix(runtimecell): start a packed cell's members in order so a later member's factory cannot take the factory… | PiG's own defect | All platforms | Unreleased |
| BF-d80425c | fix(train): gate repairs after merging train 3 and the ordering stack | PiG's own defect | All platforms | Unreleased |
| BF-d80c1cd | Where Pi calls new URL(value), Pig now parses a ws:, wss:, ftp: or file: URL, and a bracketed IPv6… | PiG's own defect | All platforms | 0.5.0 |
| BF-d8357e2 | Cache warmer pure functions against live Pi: new TestCacheWarmerPureFunctionsMatchPi… | PiG's own defect | All platforms | 0.5.0 |
| BF-d87b70c | A second ctx.ui.custom component opened while one is shown now opens over it at once, as in Pi, whether it… | PiG's own defect | All platforms | 0.5.0 |
| BF-d88dca7 | harness.ToolTaskInput.Kind and ToolTaskResult.Kind are the named string type harness.ToolTaskKind with… | PiG's own defect | All platforms | 0.5.0 |
| BF-d8bed22 | auth.json keeps every provider's entry as written, as Pi does | PiG's own defect | All platforms | 0.5.0 |
| BF-d8d513a | With the Bun TypeScript runtime (PIG_TS_RUNTIME=bun), /reload now evaluates an edited ES module extension… | PiG's own defect | All platforms | 0.5.0 |
| BF-d90c2f4 | Markdown now nests a **strong** run inside *emphasis* as Pi does, renders a line underlined with === or… | PiG's own defect | All platforms | 0.5.0 |
| BF-d983b74 | Anthropic requests whose strict prefer tools exceed a strict tool limit ("The compiled grammar is too… | PiG's own defect | All platforms | 0.5.0 |
| BF-d9886fe | fix(hljs): no magic-literal hits for the resume and dispatch bounds | PiG's own defect | All platforms | Unreleased |
| BF-da5d32b | fix(extension): theme palette reports the theme's own sourcePath; conformance rows for the rest of… | PiG's own defect | All platforms | Unreleased |
| BF-da8a870 | Pasting a ChatGPT sign-in callback URL with a ws:, wss: or ftp: scheme, a backslash path or an unusual… | PiG's own defect | All platforms | 0.5.0 |
| BF-da907ca | The Go program in the SDK page now compiles | PiG's own defect | All platforms | 0.5.0 |
| BF-da907ca-2 | The bundled command page (pig docs show commands) and the website's CLI reference now agree with `pig… | PiG's own defect | All platforms | 0.5.0 |
| BF-da907ca-3 | pig docs show divergences lists D110 and D111, and the README names the pinned Pi release (1.1.0) and its… | PiG's own defect | All platforms | 0.5.0 |
| BF-da907ca-4 | The Termux page's link to the Quickstart reaches its "Authenticate a provider" section | PiG's own defect | Android and Termux | 0.5.0 |
| BF-dac3cd5 | A session keeps reading its history as it loaded it when another program rewrites, truncates, replaces or… | PiG's own defect | All platforms | 0.5.0 |
| BF-dae37be | fix: the input loop never waits for the Session lock; the abort-fix leak and two stale tests | PiG's own defect | All platforms | Unreleased |
| BF-db12780 | fix(agent): end the tool flow at custom user messages and hold system messages mid-flow | PiG's own defect | All platforms | Unreleased |
| BF-db1ee65 | A lone UTF-16 surrogate in a tool result's details, in a nested tool call's result, or in a code mode call's… | PiG's own defect | All platforms | Unreleased |
| BF-db7007d | A Radius gateway model's samplingParamsByThinkingLevel now reaches the pi-messages model and the composed… | PiG's own defect | All platforms | 0.5.0 |
| BF-db9a315 | Codemode peak memory is now measured and guarded through the normal tool path in a fresh process with 40… | PiG's own defect | All platforms | 0.5.0 |
| BF-dbe1ad3 | pig setup now flags a go on PATH older than Go 1.26, the extension SDK minimum, naming the version found… | PiG's own defect | All platforms | 0.5.0 |
| BF-dbe25ef | CI: make interface-gaps passes on main again | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-10 | The bug fix ledger no longer credits a squash commit's tests to a release note that names no fix commit | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-11 | Two calls of a Go, Python or Rust extension tool in one assistant message that each open a dialog now behave… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-12 | pig extension upgrade finds and rewrites every changed API of an extension in one run | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-13 | pig extension upgrade applies the rewrites that have one safe form and lists the rest with file, line and… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-14 | The Node extension runtime's ctx.modelRegistry.runtime is now a model runtime that a nested Session can… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-15 | Starting a child host could wait out its 30-second ready timeout ("child not ready in time") although the… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-16 | Fixed the chord host's Close waiting forever when a hold that a link was still delivering created an object… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-17 | pig update --extensions no longer fails when two Git Packages come from one repository | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-18 | A Go, Python or Rust extension tool that awaits a host call such as exec before its first dialog no longer… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-19 | Two calls of an extension tool in one assistant message now end in the order their tools answer, as Pi ends… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-2 | CI: the Windows test jobs no longer fail the pig extension upgrade notice tests when the temporary… | PiG's own defect | Windows | 0.5.2 |
| BF-dbe25ef-20 | A compaction, branch summary or bug-report summary request is now sent through the session's own stream… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-21 | Fixed pig --mode rpc exiting 143 instead of 0 when stdin ended while a termination signal's shutdown was… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-3 | pig extension upgrade: the command a startup notice or the upgrade offer prints now quotes a POSIX path… | PiG's own defect | Windows | 0.5.2 |
| BF-dbe25ef-4 | RPC mode no longer runs ctx.ui.setWidget component factories or emits setWidget requests carrying their… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-5 | pig piglet verify <binary> --source <name\|path> and pig piglet lineage <binary> --source <name\|path>… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-6 | pig piglet verify accepts --workspace <dir>, and pig piglet lineage --workspace now applies to the… | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-7 | A run that an extension's sendMessage with triggerTurn starts now carries the session's system prompt | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-8 | go install github.com/MichaelKinsy/PiG/cmd/pig@main builds again | PiG's own defect | All platforms | 0.5.2 |
| BF-dbe25ef-9 | A tool that an extension activates with setActiveTools now appears in the system prompt's tool list of the… | PiG's own defect | All platforms | 0.5.2 |
| BF-dc53412 | A pig_strip_bedrock_converse_stream Piglet Binary no longer links the AWS SDK through the Bedrock retry… | PiG's own defect | All platforms | 0.5.0 |
| BF-dca5763 | A notification, status or message that an extension queues on setImmediate or setTimeout(0) from inside a… | PiG's own defect | All platforms | 0.5.2 |
| BF-ddd4772 | fix(ai): a lone surrogate in a content block stays one code unit, as in JSON.parse | PiG's own defect | All platforms | Unreleased |
| BF-dde6aa6 | Cache warming no longer refreshes at once when a model's promptCache lifetime is far larger than the one-hour… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-10 | On Windows, path normalization and joining follow Node 24's reserved device name handling: a path such as… | PiG's own defect | Windows | 0.5.0 |
| BF-dde6aa6-11 | OpenDirReader on a FIFO now fails with not_directory at once instead of waiting for a writer | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-12 | Fixed the queued steering and follow-up lines, the dequeue hint and the startup hint separator to use Pi's… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-13 | A session entry timestamp that is not RFC 3339 but that JavaScript Date reads (a space for the T, an RFC 2822… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-14 | Fixed the session selector's selected row after a down or page-down key over an empty list: as in Pi the… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-15 | Fixed a failing tool call's result always opening fully expanded: like Pi, an error result collapses to the… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-16 | Fixed the tree selector's label prompt drawing an input row one cell too wide in a terminal two columns wide… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-17 | Fixed the card of a tool call for a tool that does not exist (a name the model made up): it now shows the… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-2 | Fixed context edits replacing the content of a system message: as in Pi's projectContextEntry, only user… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-3 | Fixed the edit tool's diff card for files with CRLF line endings: a diff row that ends in a carriage return… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-4 | Fixed the extension editor dialog to size its text window from the terminal rows (`max(5, floor(rows *… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-5 | Fixed lists drawn by the submenu and filterable selectors (settings submenus, model and thinking lists)… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-6 | Fixed the footer's context percentage keeping the usage of a reply that a context edit had dropped (a… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-7 | A table delimiter row on a lazy blockquote line no longer splits the quoted paragraph above it unless it… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-8 | A setext underline on a lazy line after another lazy line in a blockquote now renders as Pi does, joined to… | PiG's own defect | All platforms | 0.5.0 |
| BF-dde6aa6-9 | A line of only spaces and tabs that starts with a tab or four spaces, alone or after a blank line or in a… | PiG's own defect | All platforms | 0.5.0 |
| BF-de0aaa7 | RPC mode now writes the PIG_PROFILE files when it exits; it ended the process before they were written | PiG's own defect | All platforms | 0.5.0 |
| BF-de38ce2 | fix(ai,durable): a lone UTF-16 surrogate survives the JSON of a tool result, a nested call result and a… | PiG's own defect | All platforms | Unreleased |
| BF-de85b0e | An HTTP MCP client opens its server-to-client stream before it sends the next request after… | PiG's own defect | All platforms | 0.5.0 |
| BF-de91c76 | When an MCP server drops tools or is disabled, its tools are hidden in the order the server listed them | PiG's own defect | All platforms | 0.5.0 |
| BF-de91c76-2 | The error of an MCP stdio server that fails to start ends with the last 2000 UTF-16 code units of its… | PiG's own defect | All platforms | 0.5.0 |
| BF-deadeb8 | A prompt in a long session no longer costs time in proportion to the session's history: the current branch… | PiG's own defect | All platforms | 0.5.0 |
| BF-deadeb8-2 | Extension session reads (getEntry, getLabel, getChildren, getBranch, buildSessionContext and the… | PiG's own defect | All platforms | 0.5.0 |
| BF-debe30e | A keybindings.json key named like a property every JavaScript object inherits (constructor, toString… | PiG's own defect | All platforms | 0.5.0 |
| BF-dec54eb | fix(tui): Container.Render is safe beside a frame that borrows the window | PiG's own defect | All platforms | Unreleased |
| BF-df0b21c | A Go, Rust or Python extension that calls parseFrontmatter or Date.parse while its factory runs, in a… | PiG's own defect | All platforms | 0.5.0 |
| BF-df96b6a | Fix in shipped code | PiG's own defect | All platforms | Unreleased |
| BF-dfa57b3 | An OpenAI Responses request for a conversation with no messages sends "input": [] instead of "input": null | PiG's own defect | All platforms | 0.5.0 |
| BF-e098463 | A Gemini stream chunk with a member of an unexpected type is read as Pi's JavaScript reads it instead of… | PiG's own defect | All platforms | 0.5.0 |
| BF-e1764c3 | A live /skill: message renders as the collapsed [skill] name block instead of the raw <skill> XML | PiG's own defect | All platforms | 0.5.0 |
| BF-e1ad272 | The xAI, Kimi Code, Meta, Radius, OpenAI Codex, Sign in with ChatGPT and GitHub Copilot OAuth requests send… | PiG's own defect | All platforms | 0.5.0 |
| BF-e24487e | fix(cli): PIG_VERSION_FORMAT=pi prints the pinned Pi package version | PiG's own defect | All platforms | Unreleased |
| BF-e25a09f | A tool argument or event payload with a number outside the double range (1e400), at any nesting depth, no… | PiG's own defect | All platforms | 0.5.0 |
| BF-e2db931 | On Windows, refreshing an MCP server's OAuth token no longer fails with EPERM when another pig process is… | PiG's own defect | Windows | 0.5.0 |
| BF-e32c60c | A Piglet or Package installed under a symlinked agent directory keeps the origin integrity recorded in its… | PiG's own defect | All platforms | 0.5.0 |
| BF-e3f39b7 | Automatic retry now triggers for the gateway error "Unable to forward upstream stream" | PiG's own defect | All platforms | 0.5.0 |
| BF-e42ae82 | SettingsManager structured getters (settings-manager.ts getBranchSummarySettings, getRetrySettings… | PiG's own defect | All platforms | 0.5.0 |
| BF-e48a02f | fix(tui): filterable list drops the label when no cell remains (Pi select-list) | PiG's own defect | All platforms | Unreleased |
| BF-e51bb37 | OpenAI Responses streams follow Pi's processResponsesStream on malformed and edge events, checked against… | PiG's own defect | All platforms | 0.5.0 |
| BF-e54c2a7 | openai-decisions writes the image input's user message, text part and image parts with their members in… | PiG's own defect | All platforms | 0.5.0 |
| BF-e55895a | Durable: opening a store that pi-durable 1.3.0 wrote no longer leaves you unable to go back to it | PiG's own defect | All platforms | 0.5.2 |
| BF-e55cdc2 | The Codex catalog entry for GPT-6.1 Sol reports the 500K context window the Codex models endpoint returns… | PiG's own defect | All platforms | 0.5.0 |
| BF-e59f3f6 | Fixed tool-call arguments losing a field and every field after it when the model wrote an invalid \u escape… | PiG's own defect | All platforms | 0.5.0 |
| BF-e5a345e | An Anthropic request holds a mid-conversation system update until an assistant message that is actually sent… | A fix Pi shipped that PiG ported | All platforms | 0.5.3 |
| BF-e5aae63 | The editor now enters bash mode (bash border color, Escape clears it) when the text starts with ! after any… | PiG's own defect | All platforms | 0.5.0 |
| BF-e5f9fdd | The provider retry transport no longer replaces Body on the caller's request when it retries | PiG's own defect | All platforms | 0.5.0 |
| BF-e63c61c | fix(interactive): pump the owner loop while model selection awaits extensions (#208) (#209) | PiG's own defect | All platforms | Unreleased |
| BF-e64d2cf | fix(tui): a tool error result collapses like any other (Pi updateResult) | PiG's own defect | All platforms | Unreleased |
| BF-e69d018 | Closing an env connection, or losing it, now ends its daemon process with SIGTERM, as in Pi, instead of… | PiG's own defect | All platforms | 0.5.0 |
| BF-e73e536 | Codemode scripts no longer fail immediately when both codemode workers are busy, for example with parallel… | A fix Pi shipped that PiG ported | All platforms | 0.5.2 |
| BF-e75a8b9 | The external editor (ctrl+g) now edits prompt.md inside a private pi-editor-* directory under the… | PiG's own defect | All platforms | 0.5.0 |
| BF-e79c70e | fix(ai): Codex WebSocket, OpenRouter images, pi-messages and replaced Anthropic bodies write JSON.stringify… | PiG's own defect | All platforms | Unreleased |
| BF-e8489d4 | A key hint for an app action the user unbound shows no key, as in Pi, instead of the action's default key | PiG's own defect | All platforms | 0.5.0 |
| BF-e90ac19 | Very long streamed answers no longer slow down with their length: each streamed frame renders only the last… | PiG's own defect | All platforms | 0.5.0 |
| BF-e9332c8 | fix(codingagent): session search re: reads patterns as V8 does | PiG's own defect | All platforms | Unreleased |
| BF-e9392fd | A Google Generative AI or Vertex request with no contents (a history of system messages only, or one user… | PiG's own defect | All platforms | 0.5.0 |
| BF-e973464 | A fused Go extension's createLocalBashOperations().exec (and createLocalPowerShellOperations) no longer… | PiG's own defect | Windows | 0.5.3 |
| BF-e9e9cf8 | An extension that calls a pi-ai function such as completeSimple at top level or in its factory no longer… | PiG's own defect | All platforms | 0.5.0 |
| BF-ea6db99 | A provider request that fails in transport now records its cause: the failed message carries a… | PiG's own defect | All platforms | 0.5.0 |
| BF-ea90fb3 | A running !cmd block keeps only the last 2000 lines or 50 KB it shows instead of every byte the command… | PiG's own defect | All platforms | 0.5.0 |
| BF-ead285d | Two compact commands of one RPC input batch no longer cancel each other; they run one after another (D147) | PiG's own defect | All platforms | 0.5.0 |
| BF-eb34085 | The find tool trims each fd result line like JavaScript trim(): a name ending in U+FEFF loses it and a name… | PiG's own defect | All platforms | 0.5.0 |
| BF-ebb4c0e | An "m" operation whose permutation a Go caller builds as []int validates and applies again, as it did in… | PiG's own defect | All platforms | 0.5.0 |
| BF-ebb4c0e-2 | durable.DocumentContent decoded by encoding/json keeps the key order of the objects its operations carry… | PiG's own defect | All platforms | 0.5.0 |
| BF-ebbdfe7 | fix(extensions): load highlight.js grammars after the startup session_start, as Pi does | PiG's own defect | All platforms | Unreleased |
| BF-ebec8e2 | A URL host with an xn-- label that is empty after IDNA mapping (ws://xn--。, https://..\ufeffxn--) or… | PiG's own defect | All platforms | 0.5.0 |
| BF-ebfeed0 | getSystemPromptOptions().hiddenTools lists hidden tools in the order the prepareLoadout hooks hid them… | PiG's own defect | All platforms | 0.5.0 |
| BF-ec2080a | A subprocess extension's ctx.ui.custom component now learns when another component shown over it takes… | PiG's own defect | All platforms | 0.5.0 |
| BF-ed05099 | An extension's ui.setHiddenThinkingLabel now changes the label shown for hidden thinking in the chat… | PiG's own defect | All platforms | 0.5.0 |
| BF-ed05099-2 | Tab at the start of a line no longer forces file completion while the text before the cursor is a… | PiG's own defect | All platforms | 0.5.0 |
| BF-ed05099-3 | pi.exec called while an extension loads, before the session binds, no longer fails with `extension… | PiG's own defect | All platforms | 0.5.0 |
| BF-ed05099-4 | Tool-call input keeps its object members in the order the model sent them through events, tool execution… | PiG's own defect | All platforms | 0.5.0 |
| BF-edb73db | fix(codingagent): one owner-overflow forwarder at a time; the drain test waits for it, not for the flag's… | PiG's own defect | All platforms | Unreleased |
| BF-edd287c | Migrating a version 1 session file that holds a line parsing to a number, string, array or true no longer… | PiG's own defect | All platforms | 0.5.0 |
| BF-eeac307 | Anthropic and Bedrock assistant messages write providerThinkingLevel after model, and write… | PiG's own defect | All platforms | 0.5.0 |
| BF-ef0f95a | OpenCode Zen and OpenCode Go use the opencode.ai/inference endpoints, which fixes OpenCode Go Gemini models… | PiG's own defect | All platforms | 0.5.0 |
| BF-efded13 | Installing a Package or Piglet from a local git:file:// source on Windows no longer fails with `fatal… | PiG's own defect | Windows | 0.5.3 |
| BF-f02269c | When an extension's before_provider_request handler replaces an Anthropic request, or a Codex WebSocket… | PiG's own defect | All platforms | 0.5.0 |
| BF-f06ab7e | A run that sendMessage({ triggerTurn: true }) starts while idle emits before_agent_start, checks… | PiG's own defect | All platforms | 0.5.0 |
| BF-f07d033 | A response stream dropped mid-run now reports terminated (cause: ...) instead of a bare terminated (D144… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-f0b910f | Extensions' ui_prompt_start and ui_prompt_end notifications are recorded in order when the host is slow… | PiG's own defect | All platforms | 0.5.3 |
| BF-f12896b | Interactive startup writes the terminal color query before its first frame, as Pi does: starting the renderer… | PiG's own defect | All platforms | 0.5.0 |
| BF-f19efb1 | A session_compact handler's passive custom message reaches the overflow retry (D138, Pi #9051) | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-f1a59f9 | An OpenAI Responses event's output_index matches an output item as a key of Pi's Map does: a string… | PiG's own defect | All platforms | 0.5.0 |
| BF-f20cc08 | A Node extension that calls pi.appendEntry many times in one handler no longer slows down with the size of… | PiG's own defect | All platforms | 0.5.0 |
| BF-f24a8ee | Fixed Markdown links not being clickable in Herdr: TERM_PROGRAM=herdr is now detected as supporting OSC 8… | PiG's own defect | All platforms | 0.5.0 |
| BF-f24a8ee-2 | Fixed the fullscreen text selection and multi-click state surviving a session switch or other transcript… | PiG's own defect | All platforms | 0.5.0 |
| BF-f25348f | A turn_end or agent_before_settle handler in an extension process receives each… | PiG's own defect | All platforms | 0.5.0 |
| BF-f2790f9 | A tui input listener or light/dark listener that is added while input is dispatched now sees that input… | PiG's own defect | All platforms | 0.5.0 |
| BF-f288d69 | MCP OAuth token and client registration responses keep empty lists, read expires_in like JavaScript's… | PiG's own defect | All platforms | 0.5.0 |
| BF-f288d69-2 | On Windows, an npm self-update no longer fails when a virus scanner or indexer holds pig.exe open for a… | A Pi defect that PiG fixed first | Windows | 0.5.0 |
| BF-f288d69-3 | ModelRuntime.Close cancels the remote model catalog's refreshes and waits for them, so none runs on after… | PiG's own defect | All platforms | 0.5.0 |
| BF-f288d69-4 | Crash attribution reads Go goroutine traces as well as JavaScript stacks, so a crash in the PiG process… | PiG's own defect | All platforms | 0.5.0 |
| BF-f29e906 | OpenAI Completions, OpenAI Responses and Codex request bodies list their fields in the order Pi writes them… | PiG's own defect | All platforms | 0.5.0 |
| BF-f2a2b34 | An unmatched tool result row in the /tree selector shows the tool's name, as in Pi ([bash]), instead of… | PiG's own defect | All platforms | 0.5.0 |
| BF-f2c8b02 | After await ctx.reload(), a command's captured pi and ctx now throw Pi's stale message for every… | PiG's own defect | All platforms | 0.5.0 |
| BF-f34b545 | Durable: resolving harness settings no longer shares the default retry policy's max agent delay between… | PiG's own defect | All platforms | 0.5.0 |
| BF-f3b4045 | .gitignore, .ignore and .fdignore rules that skills, packages and extension entry discovery read now… | PiG's own defect | All platforms | 0.5.0 |
| BF-f4d85f8 | A large file written through a tool call no longer slows down as it streams: each streamed piece of the… | PiG's own defect | All platforms | 0.5.0 |
| BF-f5ecdc2 | Settings and models.json compat objects keep the members PiG does not model, as Pi 1.2.0 does | PiG's own defect | All platforms | 0.5.0 |
| BF-f6305d9 | A new_session, fork, clone or switch_session in the same RPC batch as a compact aborts the… | PiG's own defect | All platforms | 0.5.0 |
| BF-f68aa40 | A delta.Tracker no longer keeps memory alive that Pi's tracker releases | PiG's own defect | All platforms | 0.5.0 |
| BF-f68aa40-2 | delta.CloneJSON, chord.CopyJSON and draft placements report a Go struct or pointer with Pi's message for… | PiG's own defect | All platforms | 0.5.0 |
| BF-f6e7d49 | /tree opens and searches a 300 MB session faster than Pi: it reads the session's index and each message's… | PiG's own defect | All platforms | 0.5.0 |
| BF-f6e7d49-2 | A session without a file (--no-session) keeps the records of entries behind its latest compaction… | PiG's own defect | All platforms | 0.5.0 |
| BF-f6e7d49-3 | RPC get_entries, RPC get_tree and extension session reads now fail with an error naming the entry when… | PiG's own defect | All platforms | 0.5.0 |
| BF-f73c00c | Replaying a thinking block that holds only U+FEFF drops it and one that holds only U+0085 keeps it, as… | PiG's own defect | All platforms | 0.5.0 |
| BF-f78b951 | fix(codingagent): cache the Markdown transformer list per theme and runner (TU-T6) | PiG's own defect | All platforms | Unreleased |
| BF-f7b245a | A frontend member's main region now matches the transcript after an extension's custom entry is placed before… | PiG's own defect | All platforms | 0.5.0 |
| BF-f7c4b01 | pig extension upgrade no longer changes, breaks or rejects a Go extension that already builds against the… | PiG's own defect | All platforms | 0.5.0 |
| BF-f7d701e | Tool argument validation asserts the format keyword for the formats TypeBox checks (date, time, date-time… | PiG's own defect | All platforms | 0.5.0 |
| BF-f8673e6 | Tool argument conversion for TypeBox schemas lower-cases with JavaScript rules (an İ in UNDEFİNED no… | PiG's own defect | All platforms | 0.5.0 |
| BF-f90fedc | fix(cli): satisfy errcheck and gocritic in the validate-only change | PiG's own defect | All platforms | Unreleased |
| BF-f9648c6 | On Windows, stopping a terminal reader whose input is a pipe no longer hangs | PiG's own defect | Windows | Unreleased |
| BF-f98d129 | Clearing the queued messages (RPC clear_queue, the SDK, the interactive restore-to-editor key) no longer… | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-f98d129-2 | Omitting a failed retry attempt no longer makes the context usage jump to an estimate of the whole history | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-f98d129-3 | A request carries at most the model's inputLimits.images.maxPerRequest images | A Pi defect that PiG fixed first | All platforms | 0.5.0 |
| BF-f992050 | fix(ai,durable): keep a value that is not JSON an error when a message or a nested result is encoded | PiG's own defect | All platforms | Unreleased |
| BF-fa3da97 | A wake the scheduler took at a change is still delivered to OnWake when the Session fails and seals before… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-10 | A model reply with many tool calls no longer slows down as calls finish: finished calls are not parsed again… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-11 | Durable task-graph and view watches no longer send or rebuild the whole graph or view on each commit: a… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-12 | MCP: a server's tools, the tools it stopped offering, and the resource tools now reach the session as one… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-13 | On the Cloudflare Durable host, an AsyncLocalStorage value set around a call stays visible in the work that… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-14 | A Durable Session whose commit was cancelled while it waited for the Session line now closes instead of… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-15 | An extension that registers many tools after load (pi.registerTool) no longer slows down with each… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-16 | A context event with several extension handlers is encoded once per request instead of once per handler… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-17 | Rust SDK: a context handler that returns a new list equal to the one it received now counts as a… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-18 | A Durable core session with many live tasks recomputes its next wake without walking every live task on each… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-19 | Idle and typing frames in a long interactive session no longer redraw from the header down: a frame costs the… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-2 | env/daemon: listFiles with timeoutMs 0 truncates at once on Windows too; the deadline check no longer… | PiG's own defect | Windows | 0.5.2 |
| BF-fa3da97-20 | Markdown with many inline $ signs renders in linear time, as in Pi | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-21 | Extension SDKs: reading the session branch, an entry, a label or the leaf entry right after a new entry is… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-22 | Choosing a bundled model no longer builds every model of its provider on each request, and providers in… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-23 | chord/host: a host no longer sends on another host's connection while delivering notifications, and a… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-24 | A Durable code mode tool call made just before the script launches another call or ends has started before… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-25 | Fixed pig piglet build failing on a Piglet that carries a single-file extension (a lone .ts or .js… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-26 | A provider stream could stop for good, with the turn waiting for events that never came, when the goroutine… | PiG's own defect | Windows, macOS, Linux | 0.5.2 |
| BF-fa3da97-27 | PiG no longer hangs for 30 seconds at exit when two extensions share a Node process and one is still waiting… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-28 | A Node extension whose factory failed, because it has no default export or throws while initializing, could… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-29 | A tool that streams a growing partial result no longer sends the whole result to each subscribed extension on… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-3 | done() of an overlay ui.custom component hides that component's own overlay instead of the topmost one | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-30 | A Durable core request costs the same however many requests are in flight: a notice is tested only against… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-31 | Durable tool calls that the model issues in parallel or nested no longer slow down as the live state grows: a… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-32 | With an extension turn_end handler, each tool result no longer copies the whole session history, and a… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-33 | A Durable reference harness turn no longer copies the whole conversation history to extend its context | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-34 | Durable: Submission.Wait, WaitForIdle and WaitForTask return only after every event-stream listener has… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-35 | Selecting a go.work member module that is itself an exact factory package resolves that module's factory… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-36 | A --print or --mode json run that SIGTERM or SIGHUP reaches while its bash tool is starting the shell no… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-37 | chord/host: the Node facet loader no longer writes its child entry under one fixed name in the shared… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-38 | A Durable core listing of all or terminal tasks (ScanTasks) reads one page from storage per call instead of… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-39 | Edit diffs and the edit tool with many replacements on a large file use far less memory and time: a… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-4 | A call through a remote Chord reference with a ctx from another host, or one not made by Chord, fails with… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-40 | An extension that set its header or footer, or registered a sprite or a login, from its own goroutine painted… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-41 | The terminal width and height the extension host tells extensions during /reload, and when one starts or… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-42 | Deleting members of a large Durable core JSON object no longer costs time proportional to its size for each… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-5 | Turning thinking off on Claude Haiku 5.5 and Opus 5 sends plain thinking: {type: "disabled"} without effort… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-6 | The retry classifier treats "The service is temporarily unavailable" and Bedrock's "Service unavailable: ..."… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-7 | A Progress pause longer than Node's timer maximum, or not a number, runs after 1 ms as a Node timer does | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-8 | Anthropic and Claude on Bedrock no longer lose the images of an error tool result: Pi sends them after the… | PiG's own defect | All platforms | 0.5.2 |
| BF-fa3da97-9 | A Durable tool call aborted while the Harness was still resolving the tool to run could stay running for… | PiG's own defect | All platforms | 0.5.2 |
| BF-fac4703 | The extension editor dialog (ctx.ui.editor(), /tree summary prompt) shows the hint keys the user bound… | PiG's own defect | All platforms | 0.5.0 |
| BF-fac4703-2 | TestSettingsSelectorMatchesPi walks every row under rebound, multi-key and unbound app.message.followUp /… | PiG's own defect | All platforms | 0.5.0 |
| BF-fac4703-3 | TestSelectListInputMatchesPi also runs under the Kitty keyboard protocol (CSI-u enter, escape, ctrl+j… | PiG's own defect | All platforms | 0.5.0 |
| BF-fba9b47 | fix(extension): hold the theme colors map the wire palette memo is keyed on | PiG's own defect | All platforms | Unreleased |
| BF-fbd3640 | The legacy credential migration writes auth.json and the rewritten settings.json with the key order Pi… | PiG's own defect | All platforms | 0.5.0 |
| BF-fc5baaf | pi.exec's timeout starts once the command has been spawned, as in Pi, so time spent finding and starting… | PiG's own defect | All platforms | 0.5.0 |
| BF-fc749ad | A task list item whose first line looks like a heading, quote, rule, bullet, fence, table row or HTML tag… | PiG's own defect | All platforms | 0.5.0 |
| BF-fc886e3 | Google and Vertex request bodies list their members in the order the @google/genai SDK writes them, also… | PiG's own defect | All platforms | 0.5.0 |
| BF-fce99d3 | The "Your first ten minutes" and Quickstart pages now describe the first-time setup screen (theme, sprite and… | PiG's own defect | All platforms | 0.5.0 |
| BF-fce99d4 | An npm or git -e source that fails to install stops startup and /reload before any extension factory… | PiG's own defect | All platforms | 0.5.0 |
| BF-fd4be86 | OpenAI-compatible Chat Completions keeps the "text":"" member of a user text part whose text is only a lone… | PiG's own defect | All platforms | 0.5.0 |
| BF-fd7f660 | A resumed session keeps the tools that were active when it was saved (an optional or deferred tool a command… | PiG's own defect | All platforms | 0.5.0 |
| BF-fdfb748 | A surrogate pair split across two concatenated string halves is written as its character in session files… | PiG's own defect | All platforms | 0.5.0 |
| BF-fe16108 | Provider request bodies (OpenAI Completions, OpenAI Responses, Codex, Anthropic, Google and Mistral) write… | PiG's own defect | All platforms | 0.5.0 |
| BF-fe625bb | Mistral request bodies list their members in Pi's order (model, stream, messages, ..., then the renamed… | PiG's own defect | All platforms | 0.5.0 |
| BF-fe625bb-2 | When an extension's before_provider_request handler returns a replacement body for OpenAI Completions… | PiG's own defect | All platforms | 0.5.0 |
| BF-ffcd800 | Text after an empty heading follows the same space-token rule as after any other block: blank lines or spaces… | PiG's own defect | All platforms | 0.5.0 |
| BF-ffeb469 | A long session keeps less memory for each turn it has compacted away | PiG's own defect | All platforms | 0.5.0 |
Entries
BF-003f6f0
Ctrl+U, Ctrl+K and the word-delete keys leave an open autocomplete list open, as Pi's editor does
- Source: PiG's own defect
- Symptom: Ctrl+U, Ctrl+K and the word-delete keys leave an open autocomplete list open, as Pi's editor does.
- Root cause: deleteToStartOfLine, deleteToEndOfLine, deleteWordBackwards and deleteWordForward (tui editor.ts 1624-1700) change the text and call onChange only; the list is updated by insertCharacter, handleBackspace, handleForwardDelete and moveCursor. PiG refreshed it after each kill, closing the popup Pi leaves. Found by the skills journey.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
003f6f0e70(2026-10-09) - Tests:
tui/editor_kill_keeps_autocomplete_test.go::TestKillCommandsLeaveTheOpenAutocompleteList - Release note: Ctrl+U, Ctrl+K and the word-delete keys leave an open autocomplete list open, as Pi's editor does.
BF-007c91d
Go, Python and Rust extensions: in a before_agent_start handler, the event's systemPrompt and the…
- Source: PiG's own defect
- Symptom: Go, Python and Rust extensions: in a
before_agent_starthandler, the event'ssystemPromptand the context's system prompt getter now return the prompt the shared options render when read, as Pi and Node do. - Root cause: Each SDK now holds the prompt the host rendered for the options the handler received and, once the options differ, asks the host to render them with renderSystemPrompt, reusing the result until they change again: - Go: data["systemPrompt"] is an *sdk.SystemPrompt (Get, MarshalJSON), since a map cannot run a getter; ctx.GetSystemPrompt() returns the same value. - Python: the event dict's "systemPrompt" and ctx.get_system_prompt() render. The Host answers renderSystemPrompt without a UI bridge too (an SDK session): the render is a pure function of the options the call carries. Callers of the Go string form move to Get(); the known gaps are deleted; pig-go.json is regenerated by gointerfaces.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),007c91dcbb(2026-10-13),2a055a7a3a(2026-10-13) - Tests:
coding/extension/host/subprocess/ui_bridge_test.go::TestInteractiveDialogOpensOverFocusedOverlay,coding/extension/host/subprocess/ui_prompt_test.go::TestUIBridgePromptScopeDialogOverCustomAndNoUI,extensions/sdk-py/tests/test_system_prompt_render.py,extensions/sdk/system_prompt_render_test.go::TestBeforeAgentStartSystemPromptRenderErrorIsTheReadersError,extensions/sdk/system_prompt_render_test.go::TestBeforeAgentStartSystemPromptRendersTheCurrentOptions,internal/codingagent/ext_ui_dialog_focus_test.go::TestExtensionDialogTakesFocusFromFocusedCustomOverlay,internal/codingagent/ext_ui_dialog_focus_test.go::TestExtensionDialogTakesTerminalFromFocusedRemoteOverlay,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResultand 9 more in docs/bug-ledger.json - Release note: Go, Python and Rust extensions: in a
before_agent_starthandler, the event'ssystemPromptand the context's system prompt getter now return the prompt the shared options render when read, as Pi and Node do. A handler sees its own edit tosystemPromptOptionsat once, a later handler sees it, and after a handler returnssystemPromptlater handlers read exactly that prompt; before, they read the prompt rendered before the handler ran, and the context getter returned the Session's prompt.
BF-02114ff
fix(tui): filterable list drops the label when no cell remains (Pi select-list)
- Source: PiG's own defect
- Symptom: fix(tui): filterable list drops the label when no cell remains (Pi select-list)
- Root cause: select-list.ts renderItem computes maxWidth = width - 2 - 2 without a floor and truncateToWidth returns "" for maxWidth <= 0; Pig's FilterableList floored it at 1, drawing one label cell at widths 3-4. TestFilterableListShowsNoLabelWhenNoCellRemains is red without the fix.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
02114fff5b(2026-10-11) - Tests:
internal/codingagent/settings_selector_oracle_test.go::TestSettingsSelectorMatchesPi,tui/select_filterable_test.go::TestFilterableListShowsNoLabelWhenNoCellRemains
BF-0225a43
A device-attributes reply delivered in slow reads no longer reaches the editor as input: an ESC[? prefix…
- Source: A Pi defect that PiG fixed first (divergence D143)
- Symptom: A device-attributes reply delivered in slow reads no longer reaches the editor as input: an
ESC[?prefix waits for its final byte (D143, Pi #10657). - Root cause: No part of a split DA1 reply reaches input, and the color query's DA1 is forwarded whole. A bare ESC[ is still replayed after 150 ms (Pi's terminal.test.ts); the owed-reply absorber now covers only that case.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10657
- Removed when Pi fixes it: Pi keeps a split private CSI reply buffered until its final byte.
- Fix commits:
0225a43aa5(2026-10-10),35e46b114e(2026-10-10),6ecf8a6a81(2026-10-10) - Tests:
codemode/nested_error_unreadable_test.go::TestNestedErrorsWithUnreadableNamesSerializeAsPiDoes,codemode/sandbox_test.go::TestEmbeddedSourcesMatchTheirRecordedHashes,tui/terminal_input_regression_test.go::TestTerminalNegotiationPrefixDeadlineBoundaries,tui/terminal_input_regression_test.go::TestTerminalNegotiationPrivateCSIPrefixWaitsForItsFinalByte,tui/terminal_split_reply_test.go::TestForwardTerminalConsumesAnOwedReplySplitAcrossSlowReads,tui/terminal_split_reply_test.go::TestReassemblesDelayedDA1RepliesForKeyboardAndColorQueries - Release note: A device-attributes reply delivered in slow reads no longer reaches the editor as input: an
ESC[?prefix waits for its final byte (D143, Pi #10657).
BF-0225bbf
Pressing Escape twice with only whitespace in the editor now opens the tree or fork selector, as in Pi, which…
- Source: PiG's own defect
- Symptom: Pressing Escape twice with only whitespace in the editor now opens the tree or fork selector, as in Pi, which trims the editor text before the double-Escape check.
- Root cause: onEscape runs the double-Escape shortcut when !editor.getText().trim(). PiG required an editor with no text at all.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0225bbfb95(2026-10-13) - Tests:
internal/codingagent/interactive_test.go::TestInteractiveMode_EscapeArmsTheDoubleEscapeOverAWhitespaceOnlyEditor - Release note: Pressing Escape twice with only whitespace in the editor now opens the tree or fork selector, as in Pi, which trims the editor text before the double-Escape check.
BF-022d99f
fix(tui): the login dialog's input renders at every prompt position
- Source: PiG's own defect
- Symptom: fix(tui): the login dialog's input renders at every prompt position
- Root cause: login-dialog.ts adds the same Input to the content container for each prompt and replaceInputWithSubmittedText replaces every occurrence; a cancel leaves it in place. Pig rendered only the newest slot and blank for the others.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
022d99fb6d(2026-10-10) - Tests: None recorded.
BF-037b2e3
A session message whose members are in another order than Pi writes them, or that holds members PiG does not…
- Source: PiG's own defect
- Symptom: PiG rewrote such a message in its own member order and dropped unknown members.
- Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e8cc072521(2026-10-08),433d232815(2026-10-09),037b2e327b(2026-10-10),3b5673d924(2026-10-10),63222832e9(2026-10-10),f9ef071708(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/message_shape_test.go::TestMemberScanAcceptsMessagesPiWrites,agent/message_shape_test.go::TestMemberScanAcceptsOnlyTextTheFieldsEncodeTo,agent/message_source_test.go::TestDecodedMessageInPiOrderRetainsNoText,agent/message_source_test.go::TestDecodedMessageWritesTheObjectItWasDecodedFrom,agent/message_source_test.go::TestWithEmptyContentSpreadsContentand 19 more in docs/bug-ledger.json - Release note: A session message whose members are in another order than Pi writes them, or that holds members PiG does not model, now reaches RPC
get_messages, extension events and the built session context exactly as Pi returns it: the object parsed from the file, written withJSON.stringify. Before, PiG rewrote such a message in its own member order and dropped unknown members. Context edits and handler replacements without content keep the content member in place, as the JavaScript spread does. A system message without content is kept and gets empty content, as in Pi; before, PiG dropped it. Messages Pi wrote are unaffected and retain no extra memory.
BF-03ad544
fix(tui): card of an unregistered tool is Pi's formatToolExecution
- Source: PiG's own defect
- Symptom: fix(tui): card of an unregistered tool is Pi's formatToolExecution
- Root cause: tool-execution.ts updateDisplay draws a tool without a definition (hasRendererDefinition false, e.g. a hallucinated tool name) as one Text: toolTitle name, a blank line, JSON.stringify(args, null, 2) and the ANSI-stripped result on the next line. Pig drew a compact 'x:1' summary (with sorted keys, cut at 40/80 chars) and a blank row before the result. Found by running scripted replies through the real Pi and Pig (mock OpenAI-completions server, tmux, session files compared too). TestUnregisteredToolCardIsUpstreamFormatToolExecution is red without the change; the two component-only tests that used a definition-less card for a registered tool now give it a definition.…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
03ad544995(2026-10-11) - Tests:
tui/summary_output_pad_test.go::TestToolAndBashBlocksFollowOutputPad,tui/tool_execution_test.go::TestToolExecutionStreamingFallbackUsesLeadingPreview,tui/tool_execution_test.go::TestUnregisteredToolCardIsUpstreamFormatToolExecution
BF-040088e
ai.CloudflareStreams drops the deferred fetch and cancel operations of the API it wraps, as Pi's…
- Source: PiG's own defect
- Symptom: It passed them through without materializing the Cloudflare endpoint placeholders.
- Root cause: Pi's cloudflareStreams (providers/cloudflare-stream.ts:21-29) returns an object with only stream and streamSimple. ProviderStreams declares fetchDeferred and cancelDeferred as optional members (types.ts:292-305), so the wrapper drops them and Models reports 'does not support deferred responses' (models.ts:925-927). Go copied the wrapped value, so the deferred operations passed through, and they ran without the Cloudflare placeholders materialized. An earlier change pinned the pass-through and said those operations have no Pi counterpart.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
040088eba4(2026-10-10) - Tests:
ai/classifier_http_error_name_test.go::TestClassifierHTTPErrorNameDistinguishesTimeoutFromHTTPFailure,ai/cloudflare_streams_test.go::TestCloudflareStreamsDropsDeferredOperationsAndLeavesTheWrappedValueUntouched - Release note:
ai.CloudflareStreamsdrops the deferred fetch and cancel operations of the API it wraps, as Pi'scloudflareStreamsdoes, so a Cloudflare-wrapped API reports that it does not support deferred responses. Before, it passed them through without materializing the Cloudflare endpoint placeholders.
BF-04ef0ac
Closing a Chord host no longer hangs when a hold arrives over a link that closes at the same time
- Source: PiG's own defect
- Symptom: Closing a Chord host no longer hangs when a hold arrives over a link that closes at the same time.
- Root cause: Close's last snapshot of the objects goes through loop.run, which runs ahead of the tasks posted before it and drains them after. A hold the link had handed to the loop before it closed could therefore run after that snapshot and after the connection had released everything it held (Connection.closed). It created an object nothing disposed and nothing released, and the open of its resource, which waits for the abort a disposal raises, kept waitTasks blocked for good: windows native-4, TestAwaitingAGetTimesOutAfterReadyTimeoutWithReadyTimeoutAndReleasesItsHold, Close in waitTasks for 29 minutes with the gate's open in its select for as long.
- User impact: Crash, hang or runaway process
- Platform: Windows
- Fixed in: Unreleased
- Fix commits:
04ef0acc60(2026-10-23) - Tests:
chord/host/awaiting_upstream_test.go::TestAwaitingAGetTimesOutAfterReadyTimeoutWithReadyTimeoutAndReleasesItsHold,chord/host/close_queued_hold_test.go::TestCloseDropsAHoldQueuedBeforeItsLinkClosed - Release note: Closing a Chord host no longer hangs when a hold arrives over a link that closes at the same time. A frame the link had handed to the host loop before it closed could run after Close took its last look at the host's objects and after the connection had released what it held, so it created an object nothing disposed or released, and Close waited for that object's resource open for good (seen as a 30 minute
chord/hosttest timeout on Windows after aready_timeoutget). A connection now ignores frames once its link closed, as Pi's link delivers none.
BF-051a79d
A Provider object registered by an extension (pi.registerProvider(provider)) now receives the session's…
- Source: PiG's own defect
- Symptom: A Provider object registered by an extension (
pi.registerProvider(provider)) now receives the session'sonResponseandonProviderStreamEventcallbacks in its stream options, as in Pi, soafter_provider_responseandprovider_stream_eventfire for its requests. - Root cause: Pi's session passes onPayload, onResponse and onProviderStreamEvent to modelRuntime.streamSimple (sdk.ts:385-433), and prepareRequest hands them to Provider.stream while it strips transformHeaders (model-runtime.ts: 671-689).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
051a79d333(2026-10-09),33ce911946(2026-10-11, #206) - Tests:
coding/extension/host/subprocess/native_provider_request_callbacks_test.go::TestNativeProviderSessionRequestHooks,coding/extension/host/subprocess/native_stream_callback_test.go::TestNativeStreamCallbackDispatchesByName,test/extension-conformance/provider_object_test.go::TestProviderObjectsAcrossSDKs,test/extension-conformance/provider_on_response_test.go::TestNativeProviderStreamsForwardOnResponse - Release note: A Provider object registered by an extension (
pi.registerProvider(provider)) now receives the session'sonResponseandonProviderStreamEventcallbacks in its stream options, as in Pi, soafter_provider_responseandprovider_stream_eventfire for its requests. The host answers each Provider callback by its name; before, every callback was treated asonPayload. A Go caller'sStreamOptions.OnResponsereaches the Provider fromModelRuntime.StreamandStreamSimplethe same way: the observer gets the response status, headers and model, the Provider awaits it, and an observer error ends the stream with that error. Reported by @JihongWEI, whose reproduction is now a regression test (#206).
BF-051a79d-2
A Provider object's stream options no longer carry a stray hasOnPayload value
- Source: PiG's own defect
- Symptom: A Provider object's stream options no longer carry a stray
hasOnPayloadvalue. - Root cause: Pi's session passes onPayload, onResponse and onProviderStreamEvent to modelRuntime.streamSimple (sdk.ts:385-433), and prepareRequest hands them to Provider.stream while it strips transformHeaders (model-runtime.ts: 671-689).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
051a79d333(2026-10-09) - Tests:
coding/extension/host/subprocess/native_provider_request_callbacks_test.go::TestNativeProviderSessionRequestHooks,coding/extension/host/subprocess/native_stream_callback_test.go::TestNativeStreamCallbackDispatchesByName,test/extension-conformance/provider_object_test.go::TestProviderObjectsAcrossSDKs - Release note: A Provider object's stream options no longer carry a stray
hasOnPayloadvalue.
BF-053d96d
RPC mode: when an extension command calls pi.sendUserMessage() or `pi.sendMessage(..., { triggerTurn: true…
- Source: PiG's own defect
- Symptom: PiG used to emit
agent_start,turn_startand the run's first messages before the response. - Root cause: In Pi an extension command that calls pi.sendUserMessage() without awaiting it starts prompt() inside the handler: prompt() awaits _runInputHandlers and checkAuth, then emitBeforeAgentStart calls the before_agent_start handlers (agent-session.ts:1968-2112, 2116-2140), while the command's
await command.handler()andawait this._tryExecuteExtensionCommand(text)write the prompt response two microtasks after the handler returned (rpc-mode.ts:394-413). The run's preparation starts before the response, and agent_start follows it. Under D139 (Pi PR #10739) an idle triggerTurn sendMessage calls _startRun -> _prepareRun -> emitBeforeAgentStart before its first await, so its before_agent_start… - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),053d96d1af(2026-10-12) - Tests:
coding/cli/rpc_command_send_order_test.go::TestRPCCommandTurnStartOrderMatchesPi,coding/cli/rpc_command_send_order_test.go::TestRPCInlineCommandTurnStartOrderMatchesPi,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.tomland 2 more in docs/bug-ledger.json - Release note: RPC mode: when an extension command calls
pi.sendUserMessage()orpi.sendMessage(..., { triggerTurn: true })without awaiting it, the run'sbefore_agent_starthandlers run before the command'spromptresponse, andagent_startfollows the response, as in Pi. PiG used to emitagent_start,turn_startand the run's first messages before the response.
BF-056bbfb
Key matching follows Pi for three terminal inputs
- Source: PiG's own defect
- Symptom: Key matching follows Pi for three terminal inputs.
- Root cause: A differential test runs Pi's parseKey and matchesKey in Node (testdata/keys.mjs, from the pinned .upstream/current source, WT_SESSION and SSH unset) over 7570 inputs with the Kitty protocol off and on: every legacy byte and ESC-prefixed byte, SS3/CSI cursor, editing and function sequences with modifiers, xterm modifyOtherKeys, Kitty CSI-u with shifted and base-layout alternates, modifier masks with lock bits, event types and text fields, and truncated and bit-flipped variants, each against 660 key ids. parseKey agreed on every input; matchesKey differed on 144, in three ways, all fixed in key_match.go: - keys.ts matchesKey returns false for 'escape' or 'esc' with any modifier (:832); Pig…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e8cc072521(2026-10-08),056bbfb74e(2026-10-09),2c04c1445a(2026-10-09),7c4d874438(2026-10-09),da99854d1b(2026-10-09) - Tests:
ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,chord/delta/own_key_order_test.go::TestDiffRevisionsVisitsArrayIndexKeysFirstInNumericOrder,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,internal/chord/chordjson/own_key_order_test.go::TestCopyFollowsPisKeyOrderAndMessages,tui/keys_oracle_test.go::TestParseAndMatchKeyMatchPiOnSeededInput - Release note: Key matching follows Pi for three terminal inputs. A modified
escape(ctrl+escape,shift+alt+esc) matches no key binding, as in Pi, although the same input still parses to that name. An xterm modifyOtherKeys sequence (ESC [ 27 ; mod ; code ~) matches only when its modifier is exactly the binding's, so a Caps Lock or Num Lock bit no longer matches. An unmodified letter, digit or symbol binding no longer matches a modifyOtherKeys sequence.
BF-058e7a0
The Go, Rust and Python SDKs export Pi's compaction helpers calculateContextTokens, shouldCompact…
- Source: PiG's own defect
- Symptom: The Go, Rust and Python SDKs export Pi's compaction helpers
calculateContextTokens,shouldCompact,getLastAssistantUsage,estimateTokens,findCutPoint,findTurnStartIndexandDEFAULT_COMPACTION_SETTINGS; the Rust SDK also exportsUsageInfo. - Root cause: Pi's index.ts exports calculateContextTokens, shouldCompact, getLastAssistantUsage and DEFAULT_COMPACTION_SETTINGS (core/compaction/compaction.ts) for extensions. Only the Node SDK, which runs Pi's own bundle, had them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
058e7a0e4b(2026-10-11) - Tests:
test/extension-conformance/compaction_usage_test.go::TestConformance_CompactionUsageHelpersAgreeAcrossSDKs - Release note: The Go, Rust and Python SDKs export Pi's compaction helpers
calculateContextTokens,shouldCompact,getLastAssistantUsage,estimateTokens,findCutPoint,findTurnStartIndexandDEFAULT_COMPACTION_SETTINGS; the Rust SDK also exportsUsageInfo.
BF-0630078
MCP tool calls no longer fail with "Invalid MCP tools/call structured content" when the server returns…
- Source: A fix Pi shipped that PiG ported
- Symptom: MCP tool calls no longer fail with "Invalid MCP tools/call structured content" when the server returns structured content that is not an object (an array,
null, or a primitive, as MCP 2026-07-28 allows), as Pi 67996ab7 now does. - Root cause: Ports Pi packages/mcp/src/client.ts validateCallToolResult: the "Invalid MCP tools/call structured content" rejection of a non-object is gone, so an array, null or a primitive reaches the caller (CallToolResult.structuredContent is any JSON value, protocol/content.ts).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0630078ccf(2026-10-12),3fa6e63e8e(2026-10-12) - Tests:
coding/mcpext/cli_upstream_test.go::TestMcpCommandListReportsThatAServerOffersResourcesWithoutCountingThem,coding/mcpext/connection_test.go::TestMCPConnectionsConnectsToServersWithResourcesWithoutListingThem,coding/mcpext/responsive_test.go::TestMcpDescribeStateShowsThatAServerOffersResources,coding/mcpext/tools_test.go::TestMCPToolsConvertsResultsPassingTheCallToolResultToScriptsAndFlaggingErrors,mcp/client_test.go::TestClientAcceptsStructuredContentThatIsNotAnObject,mcp/content_test.go::TestToLLMContentFallsBackToTheStructuredContentAsJSONWhenThereAreNoBlocks - Release note: MCP tool calls no longer fail with "Invalid MCP tools/call structured content" when the server returns structured content that is not an object (an array,
null, or a primitive, as MCP 2026-07-28 allows), as Pi 67996ab7 now does.
BF-0631c73
Go, Python and Rust extensions now draw an overlay component (ctx.custom with overlay: true) at the…
- Source: PiG's own defect
- Symptom: Go, Python and Rust extensions now draw an overlay component (
ctx.customwithoverlay: true) at the overlay's width, 80 columns by default, as Pi and Node extensions do. - Root cause: Pi renders a ctx.ui.custom overlay component at the width TUI.resolveOverlayLayout resolves (tui.ts:1212-1233; parseSizeValue tui.ts:228-237): min(80, available) by default, or overlayOptions width, minWidth and margin. The Node runtime already did this (runtime.mjs resolveOverlayWidth). The Go, Python and Rust SDKs rendered the component at the terminal width, so the robust-notes picker drew "robust pick (100)" in a 100-column pane where Pi draws "(80)", and a full-width line was cropped at the overlay's edge.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0631c73a3f(2026-10-09),7489735c3f(2026-10-09) - Tests:
coding/extension/host/subprocess/builder_node_entry_test.go::TestResolveNodeEntrypointImportsAnIndexMjsDirectoryAsJiti,extensions/sdk-py/tests/test_overlay_width.py,extensions/sdk/overlay_width_test.go::TestRemoteRenderWidthFollowsUpstreamOverlayLayout,test/extension-conformance/api_append_entry_test.go::TestConformance_ExtensionAPIAppendEntry,test/extension-conformance/overlay_width_conformance_test.go::TestConformance_OverlayRenderWidth - Release note: Go, Python and Rust extensions now draw an overlay component (
ctx.customwithoverlay: true) at the overlay's width, 80 columns by default, as Pi and Node extensions do. They drew it at the full terminal width, so a full-width line was cut off at the overlay's edge.
BF-064181f
fix(extension): hold the theme colors map the wire palette memo is keyed on
- Source: PiG's own defect
- Symptom: fix(extension): hold the theme colors map the wire palette memo is keyed on
- Root cause: ActiveExtensionThemeWire identified the terminal-resolved colors by the address of the map theme.Colors returned, stored as a uintptr. The theme drops that map when the terminal's reported colors change, so the collector could free it and give its address to a later colors map with other values; the memo would then return the old palette and the state push would skip the theme as unchanged. The memo now holds the map it is keyed on, so the address cannot be reused while it identifies the palette.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
064181fd0a(2026-10-11) - Tests:
internal/codingagent/ext_ui_theme_palette_test.go::TestActiveExtensionThemeWireHoldsTheColorsMapItIsKeyedOn
BF-07151d0
In print and JSON mode, Node extensions in separate processes now keep Pi's order when a later extension's…
- Source: PiG's own defect
- Symptom: In print and JSON mode, Node extensions in separate processes now keep Pi's order when a later extension's
session_shutdownoragent_settledhandler awaits a timer, however busy the machine is. - Root cause: an ordering assumption, not a lost frame. Pi runs every extension on one event loop, so while the later handler awaits its timer the immediate and the due timer the earlier handler queued run first, whatever the OS does (runner.ts:1096-1123). With separate Node processes the yielding runtime sent loop_turned and went on at once ("Nothing waits for the notify"), so the earlier process's callbacks ran before the later handler's continuation only if that process was scheduled, read the forwarded turn, and reached its check and timers phases within the later handler's 100 ms timer. On a loaded host it was not: the later handler answered first, the host invalidated, and the earlier callbacks ran…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
07151d01a7(2026-10-13) - Tests:
coding/extension/host/subprocess/loop_turned_forward_test.go::TestLoopTurnIsTakenOnlyOnceEveryRecipientTookIt,coding/extension/host/subprocess/loop_turned_forward_test.go::TestLoopTurnedReachesOnlyOtherNodeProcesses,coding/extension/host/subprocess/quit_handler_yield_test.go::TestPrintModeQuitHandlerReportsItsYield,coding/extension/host/subprocess/runtime_node_loop_turn_stall_test.go::TestNodeLoopTurnWaitsUntilAStalledProcessTookIt,coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodePromptEndWaitEndsWhenAnotherProcessTurnsTheLoop - Release note: In print and JSON mode, Node extensions in separate processes now keep Pi's order when a later extension's
session_shutdownoragent_settledhandler awaits a timer, however busy the machine is. The immediate and the timer an earlier extension's handler queued ran before that handler resumed only if the earlier process read the forwardedloop_turnedin time; a process the OS kept waiting ran them after the handler answered, with a stale ctx once the runner was invalidated. The yielding process now stays in its turn until every other Node process reports it took the turn (loop_turn_taken), after the immediates and due timers it queued before the turn ran.
BF-0790d5e
String.prototype.trim vs strings.TrimSpace: four Pi .trim() sites used Go's strings.TrimSpace, which…
- Source: PiG's own defect
- Symptom:
String.prototype.trimvsstrings.TrimSpace: four Pi.trim()sites used Go'sstrings.TrimSpace, which trims U+0085 and not U+FEFF where JavaScript trims U+FEFF and not U+0085 (jsstring.Trimis the JS rule, already used in 100 other places). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0790d5eae8(2026-10-11) - Tests:
agent/js_trim_test.go::TestReplayAndProxyLinesTrimLikeJavaScript,tui/js_trim_test.go::TestAssistantMessageComponentTrimsRunsLikeJavaScript,tui/js_trim_test.go::TestTextBlankCheckTrimsLikeJavaScript - Release note:
String.prototype.trimvsstrings.TrimSpace: four Pi.trim()sites used Go'sstrings.TrimSpace, which trims U+0085 and not U+FEFF where JavaScript trims U+FEFF and not U+0085 (jsstring.Trimis the JS rule, already used in 100 other places). Fixed where the difference is observable:Textblank check (tui/text.go, text.ts:57), assistant-message text/thinking runs (tui/assistant_message_block.go, assistant-message.ts:110-125: a leading BOM is now trimmed before the markdown renders, U+0085 is kept), replayed empty-thinking filter (agent/transform.go, transform-messages.ts:111) and the proxydata:line (agent/proxy.go, proxy.ts:181). RegressionsTestTextBlankCheckTrimsLikeJavaScript,TestAssistantMessageComponentTrimsRunsLikeJavaScript,TestReplayAndProxyLinesTrimLikeJavaScript(each red with the old call, 3 sites checked by swapping the call back).parseStreamingJsonValue's guard (ai/streaming_json.go) was probed and left: the result is the same either way. Left for owners:stripThinkingSignatures(ai/anthropic.go, PiG's own D37 recovery, no Pi counterpart), the Pi.trim()sites in other providers if any still useTrimSpace../tui -race(1,219 s) failed only the flakyTestSurfaceRendersAnEditorBeforeAnythingAsksForItsTerminal(race intui_surface_program_status_test.go:213, passes alone) and./agentonlyTestAgentClassMatchesPi/TestAgentLoopMatchesPi(the oracle cannot import@earendil-works/pi-aifromdurable/interop, a missing node_modules in this worktree).
BF-081e8c3
Bug report files against live Pi: new TestBugReportFilesMatchPi…
- Source: PiG's own defect
- Symptom: Bug report files against live Pi: new
TestBugReportFilesMatchPi(internal/codingagent/bug_report_files_oracle_test.go;collectBugReportDiagnosticsover aSessionManageropened on 500 generated session files plus a crash log, andbugReportFiles; bytes of diagnostics.json, member list, session.jsonl and summary.md). - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
081e8c34d2(2026-10-11) - Tests:
ai/diagnostic_empty_details_test.go::TestAssistantMessageDiagnosticKeepsAnEmptyDetailsObject,internal/codingagent/bug_report_files_oracle_test.go::TestBugReportFilesMatchPi - Release note: Bug report files against live Pi: new
TestBugReportFilesMatchPi(internal/codingagent/bug_report_files_oracle_test.go;collectBugReportDiagnosticsover aSessionManageropened on 500 generated session files plus a crash log, andbugReportFiles; bytes of diagnostics.json, member list, session.jsonl and summary.md). Two real fixes, both red first (128 of 500, then 13): (1) report.json and diagnostics.json went throughjson.Encoder, which writes U+2028/U+2029 as\u2028whereJSON.stringifywrites them literally (and kept Go number text);bugReportJSONnow canonicalises throughstringifyIndented(internal/codingagent/bug_report.go); (2) a diagnostic'sdetails: {}was dropped byomitempty(ai.AssistantMessageDiagnosticnow has aMarshalJSONthat keeps an empty, non-nil Details; regressionTestAssistantMessageDiagnosticKeepsAnEmptyDetailsObject). 14 mutations red (crashnotifiedstrip needed the crash corpus to be a JSON array, asreadCrashLogexpects). Findings left for a ruling (questions/lg-help-4.md): PiG'sAssistantMessagecannot tell an emptyerrorMessage/rawStopReasonfrom an absent one (Pi keeps""), and a diagnostic'sdetailsis a Go map, so non-index key order is sorted, not insertion order; both are excluded from the corpus and named in the test comment. Not compared: report.json content (metadata tests), the zip container (Pi writes a classic layout, Go's archive/zip a data-descriptor layout; the members' content is what a reader sees).
BF-082355f
The session picker checks its keys in Pi's order when bindings overlap (delete before rename, list actions…
- Source: PiG's own defect
- Symptom: The session picker checks its keys in Pi's order when bindings overlap (delete before rename, list actions up, down, page up, page down, confirm, cancel), and Enter typed into the search box selects the highlighted session even when
tui.select.confirmis rebound. - Root cause: (1) tui.input.submit (ctrl+s here) must select through the search Input's onSubmit -> selectList.handleInput("\r"); Pig's host-owned TextInput only marked itself done. (2) Pig re-fired onSelect/onCancel on every later navigation key because it read the list's sticky Done state; Pi fires once per confirm/cancel key (new FilterableList.Reset, called by the selector after it dispatches). Scenario settings/06 (/thinking, m Down Left d -> '> dm') is red on the old selector ('> md') and byte-equal now.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d1f258f335(2026-10-06),082355f166(2026-10-07),0ae3fcf17c(2026-10-07),4d041349a7(2026-10-07),c8a90a61db(2026-10-07),ccaade27af(2026-10-07) - Tests:
internal/codingagent/extension_dialog_remapping_test.go::TestExtensionDialogRemappingThroughOwnerLoop,internal/codingagent/remote_overlay_production_review_test.go::TestRemoteOverlayProductionRejectsCachedTerminalWidth,internal/codingagent/session_selector_oracle_test.go::TestSessionSelectorInputMatchesPi,internal/codingagent/thinking_selector_oracle_test.go::TestThinkingSelectorInputMatchesPi,internal/codingagent/w3_pi_test.go::TestW3SessionPiComparison,test/parity/scenarios/settings/06-thinking-selector-search-editing.toml,test/parity/scenarios/settings/08-settings-search-editing.toml,tui/bordered_loader_oracle_test.go::TestBorderedLoaderMatchesPiand 12 more in docs/bug-ledger.json - Release note: The session picker checks its keys in Pi's order when bindings overlap (delete before rename, list actions up, down, page up, page down, confirm, cancel), and Enter typed into the search box selects the highlighted session even when
tui.select.confirmis rebound.
BF-082f350
markdown.codeBlockIndent: Pi's getter is markdown?.codeBlockIndent ?? " ", so an authored "" means no…
- Source: PiG's own defect
- Symptom:
markdown.codeBlockIndent: Pi's getter ismarkdown?.codeBlockIndent ?? " ", so an authored""means no indent and an authorednull(which survives the project merge and overrides the global value) reads the default. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),da99854d1b(2026-10-09),082f350aff(2026-10-10),8001cc51af(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunkand 10 more in docs/bug-ledger.json - Release note:
markdown.codeBlockIndent: Pi's getter ismarkdown?.codeBlockIndent ?? " ", so an authored""means no indent and an authorednull(which survives the project merge and overrides the global value) reads the default.MarkdownSettings.CodeBlockIndentwas a plain string, so""read as the two-space default (lg-review-i1 note on r3). Now*stringwith an authored-null flag (custom Unmarshal/Marshal), merged, cloned and encoded; the r3 oracleTestSettingsManagerGettersMatchPinow compares""andnull(red before: 4+ mismatches per 1,500 cases), regressionTestSettingsKeepAnAuthoredEmptyOrNullCodeBlockIndent(load, project merge, merged and global encode); 8 mutations red; one equivalent (none remaining).
BF-086395a
coding.DefaultAgentDir() (Pi's getAgentDir) now returns the directory named by PIG_CODING_AGENT_DIR…
- Source: PiG's own defect
- Symptom:
coding.DefaultAgentDir()(Pi'sgetAgentDir) now returns the directory named byPIG_CODING_AGENT_DIR(PI_CODING_AGENT_DIRwithPIG_USE_PI_DIRS=1), tilde expanded, as thepigbinary does; before, SDK embedders,--skillloading, the default skills directory and the interactive default ignored the variable and used$PIG_HOME/agent. - Root cause: coding.DefaultAgentDir() (the SDK entry point for Pi's getAgentDir) and the internal DefaultAgentDir it wraps returned $PIG_HOME/agent and ignored PIG_CODING_AGENT_DIR (PI_CODING_AGENT_DIR in shared mode), while the pig binary's AgentDir() read it: an embedder, --skill loading, DefaultSkillsDir and the interactive default named a different directory. The variable check moves into the one resolver; AgentDir and DefaultAgentDir are now the same function.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
086395a8bd(2026-10-11) - Tests:
coding/services_test.go::TestDefaultAgentDirHonorsTheAgentDirVariable - Release note:
coding.DefaultAgentDir()(Pi'sgetAgentDir) now returns the directory named byPIG_CODING_AGENT_DIR(PI_CODING_AGENT_DIRwithPIG_USE_PI_DIRS=1), tilde expanded, as thepigbinary does; before, SDK embedders,--skillloading, the default skills directory and the interactive default ignored the variable and used$PIG_HOME/agent.
BF-08bf076
An extension login callback (onPrompt, onSelect, onManualCodeInput) whose prompt fails for a reason…
- Source: PiG's own defect
- Symptom: Every failure reached the flow as a cancel.
- Root cause:
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
nativeExtensionProviders.get(id) ?? builtins.get(id)); registryAuthConfig now does the same, so the methods come from the retained provider object on every register, refresh and publication path; - the host carrier did not map auth.apiKey.login, auth.oauth.login or loginLabel. It now does, and their prompt and notify reverse calls decode into the closed AuthPrompt and AuthEvent unions (auth/types.ts); any other type fails the login with an…
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
08bf0761c9(2026-10-09, #201),26d302a2d7(2026-10-09, #201),da99854d1b(2026-10-09) - Tests:
coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,coding/extension/host/subprocess/native_provider_login_test.go::TestDecodeNativeLoginCallback,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeAuthInteractionAnswersTheLoginDialog,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeProviderCarrierCarriesDeclaredLogins,coding/extension/host/subprocess/native_provider_oauth_test.go::TestNativeProviderOAuthLoginCallbacks,coding/extension/host/subprocess/oauth_legacy_callbacks_test.go::TestOAuthProxy_LoginRelaysTheLegacyCallbackSurface,internal/codingagent/native_provider_login_test.go::TestNativeProviderAPIKeyLoginRunsDeclaredLogin,internal/codingagent/native_provider_login_test.go::TestNativeProviderAPIKeyLoginWithoutDeclaredLoginUsesGenericPromptand 3 more in docs/bug-ledger.json - Release note: An extension login callback (
onPrompt,onSelect,onManualCodeInput) whose prompt fails for a reason other than the user's cancel now reports that error to the flow. Before, every failure reached the flow as a cancel.
BF-093cf5c
Provider error text is written the way Pi writes it
- Source: PiG's own defect
- Symptom: Provider error text is written the way Pi writes it.
- Root cause: It found two differences in Pig, fixed here: - SafeJsonStringify wrote encoding/json text: U+2028 and U+2029 escaped, -0 as -0, integer-like keys sorted lexically. JSON.stringify writes them raw, 0, and integer keys first. It now passes the encoded text through jsonstringify.Canonicalize (JSON.parse then stringify of validated JSON, which keeps lone surrogates), so raw provider bodies (json.RawMessage) and decoded values give Pi's text. - truncateUTF16 (slice(0, limit)) dropped the first half of a surrogate pair when the cut fell inside it; JavaScript keeps the lone high surrogate, U+FFFD once encoded as UTF-8. It now keeps U+FFFD. Callers: TruncateErrorText, the Radius gateway text cap and…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
093cf5c781(2026-10-09),97e178f7f6(2026-10-09) - Tests:
ai/error_body_oracle_test.go::TestErrorBodyHelpersMatchPiOnSeededProbes,ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput - Release note: Provider error text is written the way Pi writes it. A JSON error body keeps U+2028 and U+2029 as they are instead of
\u2028, writes-0as0, keeps lone surrogates, and lists integer-like keys first (JSON.stringifyrules). Text that is cut to a length limit (error bodies, Radius gateway text, Pi message text) keeps a replacement character where the cut falls inside an emoji or other pair of UTF-16 units, as Pi'ssliceleaves half of the pair, instead of dropping the half.
BF-098e33d
Extension ctx.ui.theme.fg, bg and getBgAnsi throw Unknown theme color: <token> in every SDK for a…
- Source: PiG's own defect
- Symptom: Extension
ctx.ui.theme.fg,bgandgetBgAnsithrowUnknown theme color: <token>in every SDK for a token the palette lacks, as Pi's theme does; a theme with no palette still leaves text unstyled. - Root cause: renderHighlightedHtml formats each non-empty line of a scoped run on its own, and the CLI highlight theme colors string substitutions with the text color (utils/syntax-highlight.ts, theme.ts, #10143).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
38b0f0c036(2026-10-05),098e33da91(2026-10-06),70d13f2863(2026-10-06) - Tests:
extensions/sdk/surface_parity_test.go::TestHasUIAndUIThemeFollowReplicatedState,extensions/sdk/ui_theme_style_test.go::TestUIThemeFgAndBgPanicForAnUnknownToken,test/extension-conformance/theme_test.go::TestThemeFaintForegroundAcrossSDKs,tui/highlight_test.go::TestCliHighlightThemeColorsSubstitutionsWithTheTextColor,tui/highlight_test.go::TestHighlightCode_ColorsEachLineOfPythonDocstringsIndependently,tui/syntax_highlight_upstream_test.go::TestSyntaxHighlightInterpolationEndsWithTheString,tui/syntax_highlight_upstream_test.go::TestSyntaxHighlightInterpolationTakesTheTextColor,tui/theme_unknown_token_test.go::TestThemeTextAttributeMethodsWrapInChalkPairsand 1 more in docs/bug-ledger.json - Release note: Extension
ctx.ui.theme.fg,bgandgetBgAnsithrowUnknown theme color: <token>in every SDK for a token the palette lacks, as Pi's theme does; a theme with no palette still leaves text unstyled.
BF-0a3e8ce
In print and JSON mode, SIGTERM or SIGHUP no longer hangs when a Go, Python or Rust extension's…
- Source: PiG's own defect
- Symptom: In print and JSON mode, SIGTERM or SIGHUP no longer hangs when a Go, Python or Rust extension's
session_shutdownhandler makes a host call that waits for the agent, such aswait_for_idle. - Root cause: The test's wait is now bounded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a5524c8b01(2026-10-11),0a3e8cebe8(2026-10-12) - Tests:
coding/cli/print_signal_exit_test.go::TestPrintModeSignalDisposalRunsLikePisEventLoop - Release note: In print and JSON mode, SIGTERM or SIGHUP no longer hangs when a Go, Python or Rust extension's
session_shutdownhandler makes a host call that waits for the agent, such aswait_for_idle. Such a call lets the interrupted run continue, as a Node handler'sawaitdoes.
BF-0aa31c4
Extensions that import @earendil-works/pi-ai/models (for example for clampThinkingLevel) load in the Node…
- Source: A fix Pi shipped that PiG ported
- Symptom: Extensions that import
@earendil-works/pi-ai/models(for example forclampThinkingLevel) load in the Node extension runtime, under both the@earendil-worksand the legacy@mariozechnerscope (Pi 22dc6c26, #11156). - Root cause: Ports Pi (#11156): loader shim table, register-loader and runtime-modules lists, a pi-ai models bundle entry, the host peer, and sdk-ts declarations. Needs make vendor-pi-dist and make generate to produce sdk-bundle/models.js.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
0aa31c48e4(2026-10-20) - Tests:
coding/extension/host/subprocess/node_pi_ai_models_test.go::TestNodeExtensionImportsPiAiModelsSubpath,coding/extension/host/subprocess/required_tools_test.go::TestRequiredSDKToolsFailWhenMissing - Release note: Extensions that import
@earendil-works/pi-ai/models(for example forclampThinkingLevel) load in the Node extension runtime, under both the@earendil-worksand the legacy@mariozechnerscope (Pi 22dc6c26, #11156).
BF-0aba908
Extensions' ctx.ui.setHiddenThinkingLabel() now changes the collapsed thinking label of every assistant…
- Source: PiG's own defect
- Symptom: Extensions'
ctx.ui.setHiddenThinkingLabel()now changes the collapsed thinking label of every assistant block (it was stored but never read) and is reset when the session is replaced. - Root cause: Entries removed; the members now resolve by name (gaps 5327 -> 5291, nothing new). Root cause 2 (a real Pi difference the same audit exposed): AssistantMessageComponent.setHiddenThinkingLabel had no Go form, and the extension API ctx.ui.setHiddenThinkingLabel only stored the label on the footer where nothing read it (GetHiddenThinkingLabel had no caller), so the label of collapsed thinking was always 'Thinking...'. tui.AssistantMessageComponent.SetHiddenThinkingLabel (the empty string is a label there, as in Pi) redraws; InteractiveMode.setHiddenThinkingLabel applies the extension's label to every assistant block, newAssistantMessageBlock starts new blocks with it, and replacing the session…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0aba908b97(2026-10-07) - Tests:
internal/codingagent/hidden_thinking_label_test.go::TestExtensionHiddenThinkingLabelReachesAssistantBlocks,tui/assistant_message_label_oracle_test.go::TestAssistantMessageHiddenThinkingLabelMatchesPi - Release note: Extensions'
ctx.ui.setHiddenThinkingLabel()now changes the collapsed thinking label of every assistant block (it was stored but never read) and is reset when the session is replaced.
BF-0adc7a6
A provider that an extension registers while it loads no longer fails the load when the model registry…
- Source: PiG's own defect
- Symptom: A provider that an extension registers while it loads no longer fails the load when the model registry rejects it.
- Root cause: The Extension Host asked the model registry at once for each provider an extension registered while it loaded, and failed the whole extension load with the registry's message when the registry rejected it ("Failed to load extension: Provider x, model y: no "api" specified."). Pi's loader queues the registration; the services that bind the model runtime apply the queue after every extension loaded and report a rejection as the diagnostic
Extension "<path>" error: <message>, and the extension stays loaded (loader.ts:217-218, agent-session-services.ts:157-169, runner.ts:471-487). - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
0adc7a6152(2026-10-25) - Tests:
coding/cli/extension_host_wiring_guard_test.go::TestBuildFlushesTheExtensionHostProviderRegistrations,test/extension-conformance/provider_registration_diagnostic_test.go::TestProviderRegistrationFailureIsADiagnosticAcrossSDKs,test/parity/scenarios/extensions-runtime/88-startup-provider-registration-failure.toml - Release note: A provider that an extension registers while it loads no longer fails the load when the model registry rejects it. The host asked the registry at once and reported
Failed to load extension: Provider x, model y: no "api" specified., with thepig -nehint. Pi queues the registration and applies it after every extension has loaded. PiG now does the same: the extension stays loaded, the other registrations apply, and startup reportsError: Extension "<path>" error: Provider x, model y: no "api" specified. Set it at model level or provider level.as Pi does. A registration that an extension makes after it loaded still applies at once and returns its error to the extension.
BF-0ae3fcf
The login provider list selects with Enter when tui.select.confirm is rebound, as Pi's search input submits…
- Source: PiG's own defect
- Symptom: The login provider list selects with Enter when
tui.select.confirmis rebound, as Pi's search input submits it, and starts its search cursor at the beginning of a prefilled query. - Root cause: The search is now NewInput with OnSubmit = confirm; the redundant LF special case goes (the Input submits LF); a prefilled query puts the cursor at the start as Input.setValue does.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d1f258f335(2026-10-06),0ae3fcf17c(2026-10-07) - Tests:
internal/codingagent/extension_dialog_remapping_test.go::TestExtensionDialogRemappingThroughOwnerLoop,test/parity/scenarios/settings/08-settings-search-editing.toml,tui/extension_dialog_bindings_test.go::TestExtensionDialogsMatchPiBindingsAndPalette,tui/oauth_selector_input_test.go::TestOAuthSelectorInputMatchesPi,tui/settings_list_input_test.go::TestSettingsListAnyDelegatedKeyResetsTheSelectionToTheFirstMatch,tui/settings_list_input_test.go::TestSettingsListBoundActionsRunInUpstreamOrder,tui/settings_list_input_test.go::TestSettingsListEmptyAndSingletonNavigationBoundaries,tui/settings_list_input_test.go::TestSettingsListSearchDelegatesEditingKeysToTheInputand 3 more in docs/bug-ledger.json - Release note: The login provider list selects with Enter when
tui.select.confirmis rebound, as Pi's search input submits it, and starts its search cursor at the beginning of a prefilled query.
BF-0ae4683
PiG, the extension SDKs and the sprite selection use one configuration root: PIG_HOME, else…
- Source: PiG's own defect
- Symptom: PiG, the extension SDKs and the sprite selection use one configuration root:
PIG_HOME, else$XDG_CONFIG_HOME/pig, else~/.pig. - Root cause: Pi's JsonRevisionValidator validates object properties in Reflect.ownKeys order and throws the first failing property's TypeError (revision-validator.ts:26-33). So a state with several invalid properties always reports the same error. Go's normalize walked the map in random order, so Prepare's error changed from run to run. Example: NaN at a and a struct at b gave either 'non-finite number' or 'containers must be plain objects'.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08) - Tests: None recorded.
- Release note: PiG, the extension SDKs and the sprite selection use one configuration root:
PIG_HOME, else$XDG_CONFIG_HOME/pig, else~/.pig. The Python SDK'sconfig_homeignoredXDG_CONFIG_HOME, the Rust SDK'sconfig_homestill read the retiredGOPI_HOME, and the Go SDK, the sprite selection and several host paths each had their own copy of the rule, so an extension could read or write a different directory than the CLI. An emptyPIG_HOMEorXDG_CONFIG_HOMEnow falls through to the next choice, a leading~or~/expands to your home directory everywhere, and any other value is used as written. If the configuration root needs your home directory and PiG cannot find it, PiG reports an error and exits (pig --versionand other commands that need no root still run) instead of writing under a path relative to the working directory. Pi-sharing mode (PIG_USE_PI_DIRS=1) still moves only the agent and project directories, so a freshpigin that mode writes its documentation and stages the extension SDKs under$XDG_CONFIG_HOME/pig. Reported by @baggiiiie (#200).
BF-0b13d8b
pig auth --help names pig auth ... instead of pi auth ...
- Source: PiG's own defect
- Symptom:
pig auth --helpnamespig auth ...instead ofpi auth .... - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
0b13d8bc58(2026-10-13) - Tests: None recorded.
- Release note:
pig auth --helpnamespig auth ...instead ofpi auth ....
BF-0b13d8b-2
pig extension --help lists pig extension init --login
- Source: PiG's own defect
- Symptom:
pig extension --helplistspig extension init --login. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
0b13d8bc58(2026-10-13) - Tests: None recorded.
- Release note:
pig extension --helplistspig extension init --login.
BF-0b13d8b-3
pig --help lists pig login, pig logout, pig package, pig extension upgrade, pig reload, and `pig…
- Source: PiG's own defect
- Symptom:
pig --helplistspig login,pig logout,pig package,pig extension upgrade,pig reload, andpig diagnose. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
0b13d8bc58(2026-10-13) - Tests: None recorded.
- Release note:
pig --helplistspig login,pig logout,pig package,pig extension upgrade,pig reload, andpig diagnose.
BF-0b4e1e3
The OpenAI Codex base URL keeps the whitespace around it, as Pi does (only an all-blank value falls back to…
- Source: PiG's own defect
- Symptom: The OpenAI Codex base URL keeps the whitespace around it, as Pi does (only an all-blank value falls back to the default), and its WebSocket URL is built with the WHATWG URL parser.
- Root cause: resolveCodexWebSocketUrl/resolveCodexUrl (openai-codex-responses.ts:641-654) and baseUrlIncludesApiVersion (google-vertex.ts:415) are cut out of Pi's sources (testdata/codex_vertex_urls.mjs) and run on 4512 seeded base URLs (schemes, hosts, userinfo, ports, Codex path suffixes, Vertex version segments, whitespace Go and JavaScript trim differently). Fixed with the JS-whitespace emptiness test. resolveCodexWebSocketURL now parses with nodeurl.ParseURL and applies the https:->wss:, http:->ws: setter on the serialized href (codexWebSocketHref); the unsupported-scheme error stays after it, since Pi's WebSocket constructor rejects those schemes later. vertexBaseURLIncludesAPIVersion reads the…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0b4e1e3376(2026-10-09),97e178f7f6(2026-10-09),c4d51d5c1a(2026-10-09),d1b3ad0151(2026-10-09),d80c1cd150(2026-10-09) - Tests:
ai/codex_account_id_oracle_test.go::TestCodexAccountIDMatchesPi,ai/codex_vertex_urls_oracle_test.go::TestCodexWebSocketAndVertexVersionURLsMatchPi,ai/radius_config_oracle_test.go::TestRadiusConfigMatchesPiOnSeededProbes,ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,internal/evals/plan_oracle_test.go::TestPlanMatchesPiOnSeededProbes,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,internal/nodeurl/url_oracle_test.go::TestParseMatchesNodeURL - Release note: The OpenAI Codex base URL keeps the whitespace around it, as Pi does (only an all-blank value falls back to the default), and its WebSocket URL is built with the WHATWG URL parser. The Vertex API-version check reads the parsed URL path.
BF-0b674b3
Gemini model-id checks follow Pi's lowercasing: an id spelled with a dotted capital İ or a long ſ is no…
- Source: PiG's own defect
- Symptom: Gemini model-id checks follow Pi's lowercasing: an id spelled with a dotted capital İ or a long ſ is no longer treated as a Gemini 3 thinking-level or version-matched model, and a Gemini major version too long for an integer (such as
gemini-99999999999999999999) counts as version 3 or later, asNumber.parseIntdoes. - Root cause: usesGoogleThinkingLevel, getGeminiMajorVersion, requiresToolCallId, supportsMultimodalFunctionResponse and supportsGoogleStrictToolSampling are cut from Pi's google-shared.ts (testdata/google_model_ids.mjs) and run on 4514 seeded model ids. 139 differed (red before): Go's strings.ToLower maps İ to 'i' where toLowerCase gives 'i' plus a combining dot (so 'GEMİNİ-3' matched in Go only), the (?i) regexps folded ſ and the Kelvin sign where Pi's lowercased-then-case-sensitive regexps do not, and a version longer than an int failed Atoi where parseInt keeps a large float (gemini-99999999999999999999 is >= 3 in Pi). The ids are lowercased with jsstring.ToLower, the regexps are case-sensitive…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
093cf5c781(2026-10-09),0b4e1e3376(2026-10-09),0b674b3855(2026-10-09),97e178f7f6(2026-10-09),a1fa6d1a33(2026-10-09),c4d51d5c1a(2026-10-09),d1b3ad0151(2026-10-09) - Tests:
ai/codex_account_id_oracle_test.go::TestCodexAccountIDMatchesPi,ai/codex_vertex_urls_oracle_test.go::TestCodexWebSocketAndVertexVersionURLsMatchPi,ai/error_body_oracle_test.go::TestErrorBodyHelpersMatchPiOnSeededProbes,ai/google_model_ids_oracle_test.go::TestGoogleModelIDHelpersMatchPi,ai/node_http_proxy_oracle_test.go::TestResolveHTTPProxyURLForTargetMatchesPiOracle,ai/radius_config_oracle_test.go::TestRadiusConfigMatchesPiOnSeededProbes,ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,internal/evals/plan_oracle_test.go::TestPlanMatchesPiOnSeededProbesand 1 more in docs/bug-ledger.json - Release note: Gemini model-id checks follow Pi's lowercasing: an id spelled with a dotted capital İ or a long ſ is no longer treated as a Gemini 3 thinking-level or version-matched model, and a Gemini major version too long for an integer (such as
gemini-99999999999999999999) counts as version 3 or later, asNumber.parseIntdoes.
BF-0b85f55
The Rust SDK keeps every session entry when one entry of the session file holds an unpaired surrogate (it…
- Source: PiG's own defect
- Symptom: The Rust SDK keeps every session entry when one entry of the session file holds an unpaired surrogate (it read the whole file as empty).
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
0b85f55b31(2026-10-11) - Tests: None recorded.
- Release note: The Rust SDK keeps every session entry when one entry of the session file holds an unpaired surrogate (it read the whole file as empty).
BF-0bbe365
fix(codingagent): footer context usage refreshes on a context_edit entry (Pi getContextUsage)
- Source: PiG's own defect
- Symptom: fix(codingagent): footer context usage refreshes on a context_edit entry (Pi getContextUsage)
- Root cause: Pi's footer calls session.getContextUsage() on every render. Found by an end-to-end run of the same scripted OpenAI-completions server through real Pi and Pig; handleEntryAppended now refreshes on a context_edit entry. TestContextEditEntryRefreshesTheFooterContextUsage is red without the change.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
0bbe365cd4(2026-10-11) - Tests:
internal/codingagent/interactive_cache_warming_test.go::TestContextEditEntryRefreshesTheFooterContextUsage
BF-0bc05bf
Fixed ! and RPC bash output keeping fragments of color codes, such as a stray m, when a code was split…
- Source: PiG's own defect
- Symptom: Fixed
!and RPCbashoutput keeping fragments of color codes, such as a straym, when a code was split across output chunks. - Root cause: Ports utils/ansi.ts splitIncompleteAnsiSuffix and bash-executor.ts pendingAnsi/flushOutput: an unfinished escape sequence at the end of a chunk is held for the next chunk and flushed at exit; empty chunks are not emitted. Shared by ! and RPC bash (coding.Session.ExecuteBash). Tests port agent-session-bash-persistence.test.ts:342-396.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0bc05bf8de(2026-10-07) - Tests:
coding/session_bash_persistence_upstream_test.go::TestUpstreamBashEscapeSequencesSplitAcrossChunks,internal/codingagent/tools/sanitize_split_ansi_test.go::TestExecuteBashWithOperationsFlushesPendingANSIAtExit - Release note: Fixed
!and RPCbashoutput keeping fragments of color codes, such as a straym, when a code was split across output chunks.
BF-0ce459e
/login offers a provider object's own login methods (Pi's registerNativeProvider, the…
- Source: PiG's own defect
- Symptom:
/loginoffers a provider object's own login methods (Pi'sregisterNativeProvider, theregisterProvider(provider)overload). - Root cause: The entry said the provider lists "Sign in with an account"; the tests (TestNativeProviderLoginOffersDeclaredMethods) and oauth/30 against Pi show the flow's own loginLabel replaces that label. The carrier's prompt and event decoding started from balcsida's branch (), so the entry says so besides crediting the report.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
0ce459e879(2026-10-11) - Tests:
internal/codingagent/native_provider_login_test.go::TestNativeProviderLoginOffersDeclaredMethods - Release note:
/loginoffers a provider object's own login methods (Pi'sregisterNativeProvider, theregisterProvider(provider)overload). A provider whoseauthdeclares an OAuth method now offers its account login under the flow's ownloginLabel(the generic "Sign in with an account" label is replaced), and it stays offered after the provider's catalog refresh. Reported and diagnosed by @balcsida, whose branch the login carrier started from (#201).
BF-0d0a3ef
A turn through OpenRouter (Chat Completions or Anthropic Messages) now reports the cost OpenRouter billed…
- Source: PiG's own defect
- Symptom: A turn through OpenRouter (Chat Completions or Anthropic Messages) now reports the cost OpenRouter billed, including the upstream cost of BYOK requests, instead of the catalog-rate estimate; the HTML export shows the recorded total.
- Root cause: Ports pi-mono commit (branch open-router-cost): utils/openrouter-cost.ts, parseChunkUsage in openai-completions.ts, message_delta usage in anthropic-messages.ts, export-html template.js cost.total.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0d0a3efd10(2026-10-09),1cad63426b(2026-10-10),6875edf3a8(2026-10-10) - Tests:
ai/openai_test.go::TestParseChunkUsage_PromptCacheHitTokens,ai/openai_test.go::TestParseChunkUsage_PromptTokensDetailsPreferred,ai/openrouter_cost_test.go::TestAnthropicMessagesUsesOpenRouterReportedCost,ai/openrouter_cost_test.go::TestApplyOpenRouterReportedCost,ai/openrouter_cost_test.go::TestIsOpenRouterModel,ai/openrouter_cost_test.go::TestOpenAICompletionsUsesOpenRouterReportedCost,internal/codingagent/export/export_cost_total_test.go::TestExportHTMLCostUsesRecordedTotals,internal/codingagent/export/upstream_test.go::TestExportHTMLPinnedAssets - Release note: A turn through OpenRouter (Chat Completions or Anthropic Messages) now reports the cost OpenRouter billed, including the upstream cost of BYOK requests, instead of the catalog-rate estimate; the HTML export shows the recorded total. This ports pi-mono's unmerged
open-router-costbranch (D124, pi-mono #9980).
BF-0d13b01
SettingsManager.getTransport and fifteen sibling getters (settings-manager.ts: getSteeringMode…
- Source: PiG's own defect
- Symptom:
SettingsManager.getTransportand fifteen sibling getters (settings-manager.ts: getSteeringMode, getFollowUpMode, getDefaultThinkingLevel, getTheme, getDefaultProvider, getDefaultModel, getHideThinkingBlock, getCompactionEnabled, getImageAutoResize, getBlockImages, getShowImages, getEditorPaddingX, getAutocompleteMaxVisible, getCodeBlockIndent, getEnableSkillCommands): new live-Pi oracleTestSettingsManagerGettersMatchPi(internal/codingagent/settings_getters_oracle_test.go, 1,500 global/project file pairs setting, omitting or conflicting on each key, nested objects merged one level) compares every getter's value, defaults included, with Pi's. - Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
0d13b01176(2026-10-10) - Tests:
internal/codingagent/settings_getters_oracle_test.go::TestSettingsManagerGettersMatchPi - Release note:
SettingsManager.getTransportand fifteen sibling getters (settings-manager.ts: getSteeringMode, getFollowUpMode, getDefaultThinkingLevel, getTheme, getDefaultProvider, getDefaultModel, getHideThinkingBlock, getCompactionEnabled, getImageAutoResize, getBlockImages, getShowImages, getEditorPaddingX, getAutocompleteMaxVisible, getCodeBlockIndent, getEnableSkillCommands): new live-Pi oracleTestSettingsManagerGettersMatchPi(internal/codingagent/settings_getters_oracle_test.go, 1,500 global/project file pairs setting, omitting or conflicting on each key, nested objects merged one level) compares every getter's value, defaults included, with Pi's. No bug found. 8 mutations red (transport/steering/follow-up defaults, showImages default, compaction/blockImages/hideThinking inversions, editorPaddingX). Not compared: environment-reading getters (hardware cursor, clear on shrink), wrong-typed values and fractional numbers (Settings is typed), and a codeBlockIndent of "" (Go's string cannot tell "" from absent, so GetCodeBlockIndent returns the default where Pi keeps ""). Closes the flip:c2cc995c row (SettingsManager.GetTransport) at v1.1.0.
BF-0d1c973
tui.Box no longer invalidates its children when a child is added or removed or the box is cleared
- Source: PiG's own defect
- Symptom:
tui.Boxno longer invalidates its children when a child is added or removed or the box is cleared. - Root cause: box.ts addChild, removeChild and clear call only invalidateCache, which drops the box's own cache. Pi invalidates the children only from Box.invalidate. Go's Box.AddChild, RemoveChild and Clear called b.Invalidate(), which also invalidates every child. A child component, including an extension's remote component, therefore received invalidate calls that Pi never makes. Pig's Container already follows Pi here.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0d1c97335f(2026-10-10) - Tests:
tui/box_semantics_test.go::TestBoxChangesMarkItDirty,tui/box_semantics_test.go::TestBoxStructureChangesDoNotInvalidateTheChildren - Release note:
tui.Boxno longer invalidates its children when a child is added or removed or the box is cleared. Pi'sBoxdrops only its own render cache there, and an extension component inside a box no longer receives aninvalidatecall that Pi does not make.Box.Invalidatestill invalidates every child.
BF-0d55b30
A panicking notify while MCP reports startup problems is shown as "MCP failed to load" instead of ending the…
- Source: PiG's own defect
- Symptom: A panicking notify while MCP reports startup problems is shown as "MCP failed to load" instead of ending the process, as Pi's startup chain catches it.
- Root cause:
- mcpext: let ctx.notify panics propagate everywhere except the "MCP failed to load" notify, but Pi's session_start chain
.then(() => reportProblems(ctx)).catch(...)(mcp/index.ts:1138-1147) also catches a throw from reportProblems' notify. PiG runs that report on a background goroutine, so the panic ended the process. reportStartupProblems shows it as "MCP failed to load: <message>" and ignores a second throw. - sdk-py: the Go SDK now reports a SetInvalidate(nil) panic on overlay stop in the extension log; Python's _dispose_remote_component still swallowed a raising set_invalidate(None) silently. It reports it on stderr and still disposes.
- mcpext: let ctx.notify panics propagate everywhere except the "MCP failed to load" notify, but Pi's session_start chain
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0d55b30035(2026-10-09),448ff71f1f(2026-10-09) - Tests:
automation/ci/divguard/divguard_test.go::TestBareRecoverScansEveryProductionPackage,coding/mcpext/notify_panic_internal_test.go::TestNotifyPanicPropagatesExceptOnTheLoadFailurePath,coding/mcpext/regression_test.go::TestStartupProblemsReportPanicShowsAsLoadFailure,coding/session_event_panic_test.go::TestReportExtensionErrorNamesAPanickingListener,extensions/sdk-py/tests/test_component_cleanup.py,extensions/sdk/recovered_panic_reporting_test.go::TestInvalidatorPanicOnStopIsReported,extensions/sdk/recovered_panic_reporting_test.go::TestSameOverlayComponentNeverPanicsOnUncomparableValues,extensions/sdk/recovered_panic_reporting_test.go::TestTerminalInputHandlerPanicIsReportedAndYieldsNoVerdict - Release note: A panicking notify while MCP reports startup problems is shown as "MCP failed to load" instead of ending the process, as Pi's startup chain catches it.
BF-0d55b30-2
The Python SDK reports a component whose set_invalidate(None) raises during cleanup in the extension log…
- Source: PiG's own defect
- Symptom: The Python SDK reports a component whose
set_invalidate(None)raises during cleanup in the extension log, as the Go SDK does. - Root cause:
- mcpext: let ctx.notify panics propagate everywhere except the "MCP failed to load" notify, but Pi's session_start chain
.then(() => reportProblems(ctx)).catch(...)(mcp/index.ts:1138-1147) also catches a throw from reportProblems' notify. PiG runs that report on a background goroutine, so the panic ended the process. reportStartupProblems shows it as "MCP failed to load: <message>" and ignores a second throw. - sdk-py: the Go SDK now reports a SetInvalidate(nil) panic on overlay stop in the extension log; Python's _dispose_remote_component still swallowed a raising set_invalidate(None) silently. It reports it on stderr and still disposes.
- mcpext: let ctx.notify panics propagate everywhere except the "MCP failed to load" notify, but Pi's session_start chain
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0d55b30035(2026-10-09),448ff71f1f(2026-10-09) - Tests:
automation/ci/divguard/divguard_test.go::TestBareRecoverScansEveryProductionPackage,coding/mcpext/notify_panic_internal_test.go::TestNotifyPanicPropagatesExceptOnTheLoadFailurePath,coding/mcpext/regression_test.go::TestStartupProblemsReportPanicShowsAsLoadFailure,coding/session_event_panic_test.go::TestReportExtensionErrorNamesAPanickingListener,extensions/sdk-py/tests/test_component_cleanup.py,extensions/sdk/recovered_panic_reporting_test.go::TestInvalidatorPanicOnStopIsReported,extensions/sdk/recovered_panic_reporting_test.go::TestSameOverlayComponentNeverPanicsOnUncomparableValues,extensions/sdk/recovered_panic_reporting_test.go::TestTerminalInputHandlerPanicIsReportedAndYieldsNoVerdict - Release note: The Python SDK reports a component whose
set_invalidate(None)raises during cleanup in the extension log, as the Go SDK does.
BF-0dff068
An OpenAI Responses function call streamed with an empty call_id no longer makes every later request fail…
- Source: A Pi defect that PiG fixed first (divergence D129)
- Symptom: An OpenAI Responses function call streamed with an empty
call_idno longer makes every later request fail with an emptycall_id: it gets a generated id, and a Responses request omits a stored call without one (D129, pi-mono #10299, Responses path). - Root cause: Pi replays that call on every target except Responses: Chat Completions normalizes it to _fc_1 (openai-completions.ts:1210-1215) and Gemini sends it unchanged. Only the Responses replay splits the id and sends call_id "". The omission now runs in prepareResponsesTranscript after ID normalization (omitEmptyResponsesCallIDs), through the same D120 tool flow. D129 gets its divergences bundle row (docs-drift).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10299
- Removed when Pi fixes it: Pi fills the id at stream end and omits unreplayable calls on replay, for the Responses id form too.
- Fix commits:
0dff068cfd(2026-10-10),66765600a6(2026-10-10),ed55a199f8(2026-10-10) - Tests:
ai/openai_responses_call_id_oracle_test.go::TestOpenAIResponsesToolCallIDPartsMatchPi,ai/openai_responses_call_id_oracle_test.go::TestStoredEmptyCallPartReplaysToOtherTargetsAsPi,ai/openai_responses_idless_tool_call_test.go::TestResponsesEmptyCallIDOmissionIsConfinedToTheResponsesReplay,ai/openai_responses_idless_tool_call_test.go::TestResponsesToolCallWithoutCallIDDoesNotWedgeReplay - Release note: An OpenAI Responses function call streamed with an empty
call_idno longer makes every later request fail with an emptycall_id: it gets a generated id, and a Responses request omits a stored call without one (D129, pi-mono #10299, Responses path). A call streamed without acall_idis stored asundefined|<item id>, as in Pi.
BF-0e05901
A provider an extension registers with refreshModels and no streamSimple streams through its API, as in Pi
- Source: PiG's own defect
- Symptom: Every request failed with "Provider ... has no stream implementation".
- Root cause: A registerProvider config with refreshModels is composed in the native model collection (provider-composer.ts:613). Two defects on that path:
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
051a79d333(2026-10-09),0e05901b4c(2026-10-09) - Tests:
coding/extension/host/subprocess/native_provider_request_callbacks_test.go::TestNativeProviderSessionRequestHooks,coding/extension/host/subprocess/native_stream_callback_test.go::TestNativeStreamCallbackDispatchesByName,coding/provider_refresh_insecure_test.go::TestRefreshModelsRegistrationKeepsInsecure,test/extension-conformance/provider_object_test.go::TestProviderObjectsAcrossSDKs - Release note: A provider an extension registers with
refreshModelsand nostreamSimplestreams through its API, as in Pi. Before, every request failed with "Provider ... has no stream implementation".
BF-0e225b4
fix(runtimecell): keep the Python runner's _run callable without the built event
- Source: PiG's own defect
- Symptom: fix(runtimecell): keep the Python runner's _run callable without the built event
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
0e225b41ec(2026-10-21) - Tests: None recorded.
BF-0e3411c
Fixed CI on main after the 0.5.1 ledger trim: the reviewed-pin ledgers (upstream-v0.84.0.json…
- Source: PiG's own defect
- Symptom: Fixed CI on
mainafter the 0.5.1 ledger trim: the reviewed-pin ledgers (upstream-v0.84.0.json,cli-v0.84.0.json,behavior-inputs-v0.84.0.json,behavior-input-mapping-v0.84.0.json) are back, sinceinterface-deltareads them as the base of the current delta; the stale testprune ledger entries and bug ledger entries orphaned by the 0.5.0 squash merge are gone. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
0e3411c597(2026-10-19, #224) - Tests: None recorded.
- Release note: Fixed CI on
mainafter the 0.5.1 ledger trim: the reviewed-pin ledgers (upstream-v0.84.0.json,cli-v0.84.0.json,behavior-inputs-v0.84.0.json,behavior-input-mapping-v0.84.0.json) are back, sinceinterface-deltareads them as the base of the current delta; the stale testprune ledger entries and bug ledger entries orphaned by the 0.5.0 squash merge are gone.
BF-0e360a0
The tui package's default keybindings no longer depend on the platform: undo is ctrl+- and the alt-screen…
- Source: PiG's own defect
- Symptom: The
tuipackage's default keybindings no longer depend on the platform: undo isctrl+-and the alt-screen prompt and search keys are Pi'sTUI_KEYBINDINGSeverywhere. - Root cause: Pi's getKeybindings() falls back to TUI_KEYBINDINGS, which binds undo to ctrl+- and the alt-screen prompt/search keys the same on every platform; only coding-agent KEYBINDINGS overrides them on Windows and WSL, and the coding agent installs that table through its own manager.
- User impact: Behavior differed from Pi
- Platform: Windows, WSL
- Fixed in: 0.5.0
- Fix commits:
0e360a044a(2026-10-06) - Tests:
internal/codingagent/tool_builtin_edit_shell_test.go::TestStockEditCardDrawsItsOwnShell,internal/codingagent/tool_builtin_renderers_pi104_test.go::TestBuiltInRenderersMatchPi104,tui/editor_oracle_test.go::TestEditorMatchesPiOracle,tui/keybindings_test.go::TestTUIDefaultKeybindingsIgnoreThePlatform,tui/login_dialog_pi_test.go::TestLoginDialogMaskDisabledMatchesPi - Release note: The
tuipackage's default keybindings no longer depend on the platform: undo isctrl+-and the alt-screen prompt and search keys are Pi'sTUI_KEYBINDINGSeverywhere. The Windows and WSL keys stay in the coding agent's table, as in Pi.
BF-0e8761b
A system message with an empty sections object is stored as "sections": {}, as Pi stores it after a reset
- Source: A fix Pi shipped that PiG ported
- Symptom: A system message with an empty sections object is stored as
"sections": {}, as Pi stores it after a reset. - Root cause: Hidden declarations stay out of the tool list, rules and skills hint through the options' hiddenTools, and codemode shows each listed tool's prompt guidelines with its declaration. ToolLoadout gains getPromptGuidelines in the Go host and in the Go, Python, Rust and Node extension SDKs, with a conformance row that reads a distinct guideline list per SDK (system-prompt.ts hiddenTools, skills.ts formatSkillsForPrompt, agent-session.ts getPromptGuidelines, codemode tool.ts).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0e8761be47(2026-10-05),b728733998(2026-10-05) - Tests:
ai/system_message_sections_test.go::TestSystemMessageWritesAnEmptySectionsObjectButNotAnAbsentOne,durable/examples/tasks_compaction_examples_test.go::TestExample24Compaction,durable/interop/interop_test.go::TestGoOpensAndResumesAStoreWrittenByPiDurable,durable/interop/interop_test.go::TestPiDurableOpensAndResumesAStoreWrittenByGo,durable/interop/interop_test.go::TestScriptedSessionJsonlFilesMatchPiDurable,durable/interop/interop_test.go::TestScriptedSessionStoresMatchPiDurable,durable/interop/interop_test.go::TestScriptedSessionsViewIdentically,durable/interop/interop_test.go::TestSecondScriptedSessionStoresMatchPiDurableand 2 more in docs/bug-ledger.json - Release note: A system message with an empty sections object is stored as
"sections": {}, as Pi stores it after a reset.
BF-0ffb06a
fix(tui): diff rows with a CR are wrapped like Pi's Text
- Source: PiG's own defect
- Symptom: fix(tui): diff rows with a CR are wrapped like Pi's Text
- Root cause: Random tool cards against Pi (TestBuiltinToolCardsRandomMatchPi, red before: edit cards 33 and 468): wrapTextWithAnsi splits on /\r\n|\r|\n/, but DiffComponent only wrapped rows wider than the card.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
0ffb06a7bf(2026-10-10) - Tests:
internal/codingagent/builtin_tool_cards_oracle_test.go::TestBuiltinToolCardsMatchPi,internal/codingagent/builtin_tool_cards_oracle_test.go::TestBuiltinToolCardsRandomMatchPi
BF-116b36c
fix(extensions): answer the start-window report of a tool call that holds no window, where the report is read
- Source: PiG's own defect
- Symptom: fix(extensions): answer the start-window report of a tool call that holds no window, where the report is read
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
116b36c0cc(2026-10-21) - Tests: None recorded.
BF-11a6fcb
fix(extension): a restart adopts the new renderers; the late-registration row runs what it registered
- Source: PiG's own defect
- Symptom: fix(extension): a restart adopts the new renderers; the late-registration row runs what it registered
- Root cause: ReplaceDeclarations replaced commands, flags and shortcuts on an isolated restart or packed recovery but left the message and entry renderers the held Extension had, so a custom type the restarted process registered did not render and one only the old process registered (at load or late through registerDeclaration) stayed and then failed with "unknown renderer". It now takes source's renderers too (TestReplaceDeclarationsAdoptsTheNewRenderers, red before).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
11a6fcb8c5(2026-10-10) - Tests:
coding/extension/declaration_registry_test.go::TestReplaceDeclarationsAdoptsTheNewRenderers
BF-11b199d
Session lines (and every other JSON.stringify-mode encode) now write ill-formed UTF-8 the way Pi does: one…
- Source: PiG's own defect
- Symptom: PiG used to write one
\ufffdescape per bad byte, so"a\xe2\x82b"became"a\ufffd\ufffdb"where Pi writes"a�b". - Root cause: JSON.stringify of a string decoded from ill-formed UTF-8 writes one raw U+FFFD per WHATWG maximal subpart; PiG's session lines wrote one \ufffd escape per bad byte. The codec now counts one replacement per maximal subpart in every mode, writes it raw in the JSON.stringify mode, and rewrites nested \ufffd escapes to raw there. Marshal and MarshalJS keep the escape form. Session files written before keep their form and read back the same.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
11b199d645(2026-10-12),4e17838ae9(2026-10-12) - Tests:
ai/errors_pool_test.go::TestSafeJsonStringifyPoolDropsLargeBuffers,extensions/sdk/json/append_string_swar_test.go::TestAppendStringMatchesBytewise,extensions/sdk/json/decode_trusted_test.go::TestUnmarshalTrustedMatchesUnmarshal,extensions/sdk/json/decode_trusted_test.go::TestUnmarshalTrustedVerifiesInTests,extensions/sdk/json/lw_budget_norace_test.go::TestAppendString200KBudget,extensions/sdk/json/lw_budget_norace_test.go::TestEncodeStatePoolDropsLargeBuffers,internal/jsonstringify/fast_node_test.go::TestFastPathKeyStackBound,internal/jsonstringify/large_object_test.go::TestIsCanonicalAcceptsLargeObjectsand 10 more in docs/bug-ledger.json - Release note: Session lines (and every other JSON.stringify-mode encode) now write ill-formed UTF-8 the way Pi does: one raw U+FFFD per WHATWG maximal subpart (what a JavaScript string decoded from those bytes holds, and what JSON.stringify writes). PiG used to write one
\ufffdescape per bad byte, so"a\xe2\x82b"became"a\ufffd\ufffdb"where Pi writes"a�b". Nested\ufffdescapes from PiG's own types are written raw as well. Session files written before this change keep the old form and read back the same strings. On the extension wire (the codec'sMarshal/MarshalJS), ill-formed UTF-8 also counts one replacement per maximal subpart rather than per byte; the\ufffdescape form there is unchanged.
BF-1268115
fix(model runtime): a cancelled request to a caller-owned provider the runtime does not know is aborted, not…
- Source: PiG's own defect
- Symptom: fix(model runtime): a cancelled request to a caller-owned provider the runtime does not know is aborted, not a setup error
- Root cause: It now waits for the update that carries the output (6 of 6, runs=3).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
1268115b91(2026-10-10) - Tests:
coding/model_runtime_setup_abort_upstream_test.go::TestCancelledRequestToAnUnregisteredCallerProviderIsAborted,test/parity/scenarios/providers-faux-streaming/14-provider-immediate-abort.toml,test/parity/scenarios/rpc/55-rpc-abort-during-tool-next-request.toml
BF-1356aae
fix(train): load pig_sdk.tui with the package, run the TS queue check with no runtime, pin three fixture…
- Source: PiG's own defect
- Symptom: fix(train): load pig_sdk.tui with the package, run the TS queue check with no runtime, pin three fixture modules to the root SDK version
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
1356aae997(2026-10-21) - Tests: None recorded.
BF-1364791
Stopping a codemode script or reaching its deadline no longer waits up to half a second for a built-in that…
- Source: PiG's own defect
- Symptom: Stopping a codemode script or reaching its deadline no longer waits up to half a second for a built-in that does not poll the interrupt flag (#10748).
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
1364791228(2026-10-13) - Tests:
codemode/job_poll_test.go::TestAMicrotaskSpinEndsAtTheJobPollNotTheHardStop,codemode/lifetime_test.go::TestCloseAbortsEveryRunningExecution,codemode/lifetime_test.go::TestEveryTerminalKindClosesItsVMWithoutHaltingTheInstance,codemode/worker_admission_test.go::TestAFailedModuleLoadAcquiresNoAdmission,codemode/worker_admission_test.go::TestAFailingOnWorkerStartedEndsTheWorker,codemode/worker_admission_test.go::TestAdmissionIsReleasedOnExitNotOnTheResult,codemode/worker_admission_test.go::TestDeniedAdmissionStartsNoWorker,codemode/worker_admission_test.go::TestReportsTheActualExitOfExecuteAndStartWorkersand 3 more in docs/bug-ledger.json - Release note: Stopping a codemode script or reaching its deadline no longer waits up to half a second for a built-in that does not poll the interrupt flag (#10748).
BF-1390c9d
fix(ai): the Anthropic event reader matches the type member exactly
- Source: PiG's own defect
- Symptom: fix(ai): the Anthropic event reader matches the type member exactly
- Root cause: An earlier change read event.type through a Go struct field, which encoding/json matches case-insensitively, so {"TYPE":"message_start"} set sawMessageStart and the stream ended with 'Anthropic stream ended before message_stop', where Pi's event.type is undefined (anthropic-messages.ts:551-556). anthropicEventType reads the exact member from a map (last duplicate wins, as JSON.parse).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
1390c9d6a0(2026-10-10) - Tests: None recorded.
BF-155d0f3
In RPC mode, SIGTERM or SIGHUP during a tool call likewise stops the run while the session_shutdown…
- Source: PiG's own defect
- Symptom: In RPC mode, SIGTERM or SIGHUP during a tool call likewise stops the run while the
session_shutdownhandlers finish without waiting, as Pi does: a handler that throws no longer lets the run persist the tool result and send another provider request before the exit. - Root cause: Pi's RPC signal handler kills tracked children and awaits runtimeHost.dispose on its one event loop (rpc-mode.ts:366-376,726-744). The killed bash call's exit is I/O, so the run reaches its tool result only after a session_shutdown handler awaits a timer or I/O. PiG's run went on concurrently: with a throwing handler it persisted the tool result and an assistant turn and sent a second provider request before exiting. RPC mode now registers the same termination prelude as print mode: Session.HoldAgentEvents from the moment the signal arrives until a handler reports its yield or the Session is disposed.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
155d0f33a0(2026-10-11),329c4e7f7e(2026-10-11),34f7594aa5(2026-10-11) - Tests:
coding/cli/print_signal_shutdown_test.go::TestPrintModeSignalDeliversSessionShutdown,coding/cli/rpc_signal_disposal_test.go::TestRPCSignalDisposalHoldsTheRunLikePisEventLoop,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.tomland 1 more in docs/bug-ledger.json - Release note: In RPC mode, SIGTERM or SIGHUP during a tool call likewise stops the run while the
session_shutdownhandlers finish without waiting, as Pi does: a handler that throws no longer lets the run persist the tool result and send another provider request before the exit.
BF-15a5c0b
The Kimi Code, Meta and xAI device logins judge the verification URI the way Pi does, and Meta and xAI open…
- Source: PiG's own defect
- Symptom: The Kimi Code, Meta and xAI device logins judge the verification URI the way Pi does, and Meta and xAI open the URL Pi opens.
- Root cause: A differential test runs Pi's three private functions in Node (testdata/trusted_urls.mjs cuts them out of the pinned source) over 91 URLs and found 42 differences. Pig used url.Parse, which keeps the URL as received and accepts what the WHATWG parser rejects, and rejects forms the WHATWG parser accepts: - 'https:example.com', 'https:/example.com', 'https:///example.com' and backslash forms are valid in Pi and were refused; - ports above 65535, 'https://:443', numeric hosts out of range, a space in the host were refused by Pi and accepted; - Meta and xAI returned the URL as written, where Pi returns the href (lowercase scheme and host, default and empty port and empty userinfo dropped, empty…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
15a5c0b670(2026-10-09),7c4d874438(2026-10-09) - Tests:
ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,ai/trusted_url_oracle_test.go::TestTrustedVerificationURLsMatchPi - Release note: The Kimi Code, Meta and xAI device logins judge the verification URI the way Pi does, and Meta and xAI open the URL Pi opens. A
https:hostor backslash form is read as a URL; a port above 65535, an out-of-range numeric host, or a space in the host is refused. The opened URL is normalized asnew URLdoes (lowercase scheme and host, default port and empty userinfo dropped, an empty path becomes/) instead of being echoed as received.
BF-160d95a
In RPC mode, a run that an extension command starts with sendMessage or sendUserMessage now emits…
- Source: PiG's own defect
- Symptom: In RPC mode, a run that an extension command starts with
sendMessageorsendUserMessagenow emitsagent_startafter the command's response, as in Pi, when the command is written with the Go, Python or Rust SDK, not only on Node. - Root cause: Fix 2: rpcAdmission holds the start while a command's handler runs (rpcCommandRuns). The transport reports the handler's phase in frame order (invocation.RunPhase, Conn.reportRequestPhase): a sendMessage or sendUserMessage call resumes its parent request, because the runtime reports the call's own suspension before it sends the call, and a later blocked or suspended state suspends it. The start proceeds once the command answers (after its response is written) or awaits. In-process commands are not tracked and keep the hop placement, so TestRPCInlineCommandTurnStartOrderMatchesPi is unchanged. A Node handler that awaits something other than a host call after a send that follows its first…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),160d95a510(2026-10-13),773a868c84(2026-10-13) - Tests:
coding/cli/rpc_command_send_order_test.go::TestRPCCommandThatAwaitsTheRunItSentAnswersAfterIt,coding/cli/rpc_command_send_order_test.go::TestRPCCommandTurnStartOrderMatchesPi,coding/cli/rpc_command_send_order_test.go::TestRPCInlineCommandTurnStartOrderMatchesPi,coding/cli/rpc_ui_integration_test.go::TestRPCModeExtensionUIRoundTrip,coding/cli/session_wire_lifecycle_test.go::TestModeWireLifecycle,coding/cli/startup_alloc_budget_test.go::TestStartupAllocationBudget,coding/extension/host/subprocess/node_cell_memory_test.go::TestNodeCellMembersShareRegistryPublications,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResultand 7 more in docs/bug-ledger.json - Release note: In RPC mode, a run that an extension command starts with
sendMessageorsendUserMessagenow emitsagent_startafter the command's response, as in Pi, when the command is written with the Go, Python or Rust SDK, not only on Node. A command that awaits after the send still lets the run start first.
BF-160d95a-2
In RPC mode, the Go, Python and Rust SDKs no longer let the host move on while a notify, setStatus…
- Source: PiG's own defect
- Symptom: In RPC mode, the Go, Python and Rust SDKs no longer let the host move on while a
notify,setStatus,setWidget,setTitleor other immediate UI call is still in flight. - Root cause: Fix 2: rpcAdmission holds the start while a command's handler runs (rpcCommandRuns). The transport reports the handler's phase in frame order (invocation.RunPhase, Conn.reportRequestPhase): a sendMessage or sendUserMessage call resumes its parent request, because the runtime reports the call's own suspension before it sends the call, and a later blocked or suspended state suspends it. The start proceeds once the command answers (after its response is written) or awaits. In-process commands are not tracked and keep the hop placement, so TestRPCInlineCommandTurnStartOrderMatchesPi is unchanged. A Node handler that awaits something other than a host call after a send that follows its first…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
160d95a510(2026-10-13),773a868c84(2026-10-13) - Tests:
coding/cli/rpc_command_send_order_test.go::TestRPCCommandThatAwaitsTheRunItSentAnswersAfterIt,coding/cli/rpc_command_send_order_test.go::TestRPCCommandTurnStartOrderMatchesPi,coding/cli/rpc_command_send_order_test.go::TestRPCInlineCommandTurnStartOrderMatchesPi,coding/cli/rpc_ui_integration_test.go::TestRPCModeExtensionUIRoundTrip,coding/cli/session_wire_lifecycle_test.go::TestModeWireLifecycle,coding/cli/startup_alloc_budget_test.go::TestStartupAllocationBudget,coding/extension/host/subprocess/node_cell_memory_test.go::TestNodeCellMembersShareRegistryPublications,test/extension-conformance/rpc_nested_events_test.go::TestRPCNestedEventsCompleteInEverySDK - Release note: In RPC mode, the Go, Python and Rust SDKs no longer let the host move on while a
notify,setStatus,setWidget,setTitleor other immediate UI call is still in flight. A nested handler's UI request is now written before the response of the command that triggered it, as in Pi.
BF-1631753
Queueing a steering or follow-up message while the agent streams prints no status line, as Pi does
- Source: PiG's own defect
- Symptom: Queueing a steering or follow-up message while the agent streams prints no status line, as Pi does.
- Root cause: Pi queues a steering or follow-up message with session.prompt(text, { streamingBehavior }) and refreshes the pending-messages area (interactive-mode.ts handleSubmit, handleFollowUp 4491-4499); only a message queued during compaction shows a status (queueCompactionMessage, 4768). PiG flashed "Queued steering message" and "Queued follow-up message". Found by the queue journey.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1631753dc6(2026-10-09) - Tests:
internal/codingagent/interactive_queue_status_test.go::TestQueueingWhileStreamingShowsNoStatusLine - Release note: Queueing a steering or follow-up message while the agent streams prints no status line, as Pi does.
BF-163f7b7
Empty quoted prompt-template arguments ("" or '') are kept as empty arguments, so later positional…
- Source: PiG's own defect
- Symptom: Empty quoted prompt-template arguments (
""or'') are kept as empty arguments, so later positional arguments no longer shift and${1:-default}defaults apply (carried Pi fix, Pi 9fe3f371, #10867). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
163f7b719f(2026-10-14) - Tests:
internal/codingagent/prompt_args_random_oracle_test.go::TestPromptArgumentHandlingMatchesPiOnRandomInputs,internal/codingagent/prompt_templates_oracle_test.go::TestPromptArgumentHandlingMatchesPi,internal/codingagent/upstream_prompt_templates_test.go::TestUpstreamPromptTemplatesArguments - Release note: Empty quoted prompt-template arguments (
""or'') are kept as empty arguments, so later positional arguments no longer shift and${1:-default}defaults apply (carried Pi fix, Pi 9fe3f371, #10867).
BF-165cf58
pig package publish --to npm and pig piglet publish --to npm no longer run an npm that the published…
- Source: PiG's own defect
- Symptom: Windows'
cmd.execould pick a Package's ownnpm.cmdahead of the author's npm, and a relativenpmCommandwas resolved inside the Package directory. - Root cause: The bash tool on Windows puts the shell in a job object. When the shell exits,
processGroupMayHoldOutputasks Windows whether the job still has processes (QueryInformationJobObject,JobObjectBasicAccountingInformation). If the answer is zero,childwait.waitIdlewaits for the output pipe to reach EOF with no further bound, because an empty job means no process can still hold the pipe. - User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
2e0f88f794(2026-10-06, #160),9a95b36b11(2026-10-06, #162),a4753ec61e(2026-10-06, #170),ba9c37b247(2026-10-06, #171),c019ea9155(2026-10-06, #163),165cf5884d(2026-10-07, #180),9b63b2bc4e(2026-10-07, #176),ce8587ef40(2026-10-07, #179) - Tests:
coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,coding/cli/rpc_shutdown_test.go::TestRPCInputEndAfterExtensionCommandComparedWithPi,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleBusyEntriesAreSkippedNotErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledBeforeStartTouchesNothing,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledDuringTheLastEntryReportsTheCancellation,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleEntryLeasedAfterClassificationIsSkippedNotAnError,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleOtherErrorsStayErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleStopsWhenItsContextIsCancelledand 96 more in docs/bug-ledger.json - Release note:
pig package publish --to npmandpig piglet publish --to npmno longer run annpmthat the published directory contains. PiG finds npm in the absolute directories onPATH, so publishing from inside a Package that ships its ownnpmworks and uses your npm (or resolves a relativenpmCommandagainst the directory you runpigin) and starts that file. Before, Windows'cmd.execould pick a Package's ownnpm.cmdahead of the author's npm, and a relativenpmCommandwas resolved inside the Package directory.
BF-165cf58-2
On Windows, publishing to npm now works when npm is an npm.cmd at a path with spaces, such as `C:\Program…
- Source: PiG's own defect
- Symptom: On Windows, publishing to npm now works when npm is an
npm.cmdat a path with spaces, such asC:\Program Files\nodejs. - Root cause: The bash tool on Windows puts the shell in a job object. When the shell exits,
processGroupMayHoldOutputasks Windows whether the job still has processes (QueryInformationJobObject,JobObjectBasicAccountingInformation). If the answer is zero,childwait.waitIdlewaits for the output pipe to reach EOF with no further bound, because an empty job means no process can still hold the pipe. - User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
2e0f88f794(2026-10-06, #160),56c3f85350(2026-10-06, #172),9a95b36b11(2026-10-06, #162),ba9c37b247(2026-10-06, #171),c019ea9155(2026-10-06, #163),165cf5884d(2026-10-07, #180),9b63b2bc4e(2026-10-07, #176),ce8587ef40(2026-10-07, #179) - Tests:
agent/tool_argument_order_test.go::TestAgentLoopDeliversEditedToolArgumentsInTheHooksOrder,coding/cli/rpc_shutdown_test.go::TestRPCInputEndAfterExtensionCommandComparedWithPi,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleBusyEntriesAreSkippedNotErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledBeforeStartTouchesNothing,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledDuringTheLastEntryReportsTheCancellation,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleEntryLeasedAfterClassificationIsSkippedNotAnError,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleOtherErrorsStayErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleStopsWhenItsContextIsCancelledand 87 more in docs/bug-ledger.json - Release note: On Windows, publishing to npm now works when npm is an
npm.cmdat a path with spaces, such asC:\Program Files\nodejs. Arguments now reach the shim the way Pi's package commands pass them. AnnpmCommandthat names a script now fails with a hint to name its interpreter first.
BF-167dae3
fix(codingagent): the idle collection runs two GC cycles again so pool victims are freed (D172)
- Source: PiG's own defect
- Symptom: fix(codingagent): the idle collection runs two GC cycles again so pool victims are freed (D172)
- Root cause: An earlier change dropped the runtime.GC() before debug.FreeOSMemory(), on the claim that the second cycle returned nothing. TestIdleCollectionFreesPoolVictims replaces TestIdleCollectionRunsOneGCCycle, which pinned the regression.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
167dae3dc1(2026-10-14) - Tests:
internal/codingagent/idle_collect_cost_test.go::TestIdleCollectionFreesPoolVictims,test/perfgate/memory_rows_test.go::TestTmuxIdleMemoryGate
BF-16f8d39
fix(codingagent): keep the clock allowance on the marker header's time.Now line
- Source: PiG's own defect
- Symptom: fix(codingagent): keep the clock allowance on the marker header's time.Now line
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
16f8d392cc(2026-10-20) - Tests: None recorded.
BF-1827831
The MCP extension's tool_call handler passes a tool's reachesTools the call's input in the member order…
- Source: PiG's own defect
- Symptom: The MCP extension's
tool_callhandler passes a tool'sreachesToolsthe call's input in the member order the model wrote it, as Pi passesevent.input, instead of sorted by member name. - Root cause: Pi's shutdown() stops the TUI, awaits runtimeHost.dispose() (session_shutdown), prints the resume command when the session file exists and calls process.exit(0); it never awaits the session_start, session_before_switch or session_before_fork handler that asked for the shutdown (interactive-mode.ts:2020-2024, 4251-4290).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),1827831043(2026-10-13) - Tests:
coding/mcpext/command_test.go::TestMcpToolCallPassesTheInputInItsMemberOrderToReachesTools,coding/mcpext/extension_test.go::TestAgentSessionMCPOtherToolsDoNotWaitForServers,coding/mcpext/extension_test.go::TestAgentSessionMCPToolSearchAndTheResourceToolsWaitForEveryServer,coding/mcpext/responsive_test.go::TestMcpDirectToolCallWaitsForItsServersReconnect,internal/codingagent/context_files_oracle_test.go::TestLoadProjectContextFilesCaseVariantNamesMatchPi,internal/codingagent/context_files_random_oracle_test.go::TestLoadProjectContextFilesRandomTreesMatchPi,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeErrorand 7 more in docs/bug-ledger.json - Release note: The MCP extension's
tool_callhandler passes a tool'sreachesToolsthe call's input in the member order the model wrote it, as Pi passesevent.input, instead of sorted by member name.
BF-1848633
Extension widgets share one key namespace across extensions, as in Pi: an extension that sets a widget key…
- Source: PiG's own defect
- Symptom: Two extension processes setting the same key used to both show.
- Root cause: Pi keeps one widget map per side keyed by the widget key alone (interactive-mode.ts setExtensionWidget removes the key from both maps, then sets it), whichever extension calls it. PiG keyed extension-process widgets by "extName:key" and kept the host-process widgets apart, so two extensions setting one key both showed.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1848633092(2026-10-13) - Tests:
coding/extension/host/subprocess/host_calls_test.go::TestReadLoopWidgetPushOrdersAgainstQueuedSlotCalls,coding/extension/host/subprocess/view_terminal_parity_test.go::TestViewsLeaveTheTerminalBytesUnchanged,coding/extension/host/subprocess/widget_key_namespace_test.go::TestTwoExtensionsSettingOneWidgetKeyShareIt,internal/codingagent/widget_key_namespace_test.go::TestHostAndProcessExtensionsShareOneWidgetKeyNamespace - Release note: Extension widgets share one key namespace across extensions, as in Pi: an extension that sets a widget key another extension (in the host process or in its own process) already uses replaces that widget, and clearing the key clears it whoever set it. Two extension processes setting the same key used to both show.
BF-184ed40
fix(hljs): a language's reach does not depend on the first text it highlights; red cases for resumeLimit
- Source: PiG's own defect
- Symptom: fix(hljs): a language's reach does not depend on the first text it highlights; red cases for resumeLimit
- Root cause: Resume points were sound either way (the individual patterns, which compileMode records eagerly, bound every alternative), but which points a language offered depended on the process's history, and the over-count hid the margins: no test failed with resumeLimit's reach.lines+1 cut to 1, or with its white-space guard removed. The joined terminators now compile without recording (compileLangRe); every alternative is a pattern compileMode recorded. TestStreamedHighlightEqualsAWholeRun resumes 8,938 of 18,834 steps (7,847 before), all equal to a whole run; go test ./tui/internal/hljs and the tui highlight and Markdown tests pass. - TestResumeLimitHoldsBackPointsLaterTextDecides (apache '['…
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
184ed40f74(2026-10-11) - Tests:
tui/highlight_resume_test.go::TestStreamedHighlightEqualsAWholeRun,tui/internal/hljs/resume_test.go::TestLanguageReachDoesNotDependOnTheFirstText,tui/internal/hljs/resume_test.go::TestResumeLimitHoldsBackPointsLaterTextDecides
BF-1896331
Changing the thinking level in interactive mode (cycle key, /thinking, the settings selector) goes through…
- Source: PiG's own defect
- Symptom: Changing the thinking level in interactive mode (cycle key,
/thinking, the settings selector) goes through the Session like Pi'ssession.setThinkingLevel: the level is clamped to the model, thethinking_level_selectevent carries the clamped level, and a handler that is slow to start no longer holds the input loop. - Root cause: applyThinkingLevel no longer sets the agent, appends the entry and emits thinking_level_select from a naked goroutine (emitThinkingLevelSelect is deleted).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
18963311a1(2026-10-08) - Tests:
coding/session_extension_events_upstream_test.go::TestSessionEmitsThinkingLevelSelectOnlyForAChangeWithTheEffectiveLevel,coding/session_extension_events_upstream_test.go::TestSessionEmitsTurnStartWithThePerRunTurnIndexAndATimestamp,coding/session_extension_events_upstream_test.go::TestSessionSetThinkingLevelOnMainDispatchesOnlyTheMutation,internal/codingagent/interactive_session_append_test.go::TestSelectThinkingLevelShowsAFailedSessionAppend,internal/codingagent/thinking_selector_test.go::TestSelectThinkingLevelDoesNotHoldTheInputLoopOnTheNotification,internal/codingagent/thinking_selector_test.go::TestThinkingLevelPersistsOnlyWhenSaved,test/extension-conformance/event_payload_test.go::TestConformance_EventPayloadsReachEverySDKUnchanged,test/extension-conformance/event_unsubscribe_every_test.go::TestConformance_EventUnsubscribeEveryEvent - Release note: Changing the thinking level in interactive mode (cycle key,
/thinking, the settings selector) goes through the Session like Pi'ssession.setThinkingLevel: the level is clamped to the model, thethinking_level_selectevent carries the clamped level, and a handler that is slow to start no longer holds the input loop.
BF-18daf7f
The Go SDK's typed branch entry keeps the text, thinking and tool calls of an assistant message whose tool…
- Source: PiG's own defect
- Symptom: The Go SDK's typed branch entry keeps the text, thinking and tool calls of an assistant message whose tool calls carry object arguments (Pi's
ToolCall), and the Rust SDK'sBranchEntrydecodes Pi's nested session entries (role, text, thinking, tool calls, provider, model and usage were all empty before) and gainsid,first_kept_entry_idandUsageInfo::total_tokens. - Root cause: The host sends each session entry in Pi's shape ({"type":"message","message":{...}}), and an assistant message's content is a block array whose ToolCall blocks carry their arguments as an object (packages/ai/src/types.ts:423-431).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
18daf7fed2(2026-10-11) - Tests:
extensions/sdk/branch_entry_tool_call_test.go::TestBranchEntryDecodeMatchesTheRustSDK,extensions/sdk/branch_entry_tool_call_test.go::TestBranchEntryKeepsAnAssistantMessageWithAPiToolCall,test/extension-conformance/conformance_test.go::TestConformance_TransportsMatch,test/extension-conformance/typed_branch_entries_test.go::TestConformance_TypedBranchEntriesAgreeBetweenGoAndRust - Release note: The Go SDK's typed branch entry keeps the text, thinking and tool calls of an assistant message whose tool calls carry object arguments (Pi's
ToolCall), and the Rust SDK'sBranchEntrydecodes Pi's nested session entries (role, text, thinking, tool calls, provider, model and usage were all empty before) and gainsid,first_kept_entry_idandUsageInfo::total_tokens.
BF-18f7d39
Codemode @options errors report V8's JSON error text, tool and schema declarations keep unpaired…
- Source: A fix Pi shipped that PiG ported
- Symptom: Codemode
@optionserrors report V8's JSON error text, tool and schema declarations keep unpaired surrogates, and an explicitly empty global signature renders as Pi renders it. - Root cause: Pi packages/codemode/src/source.ts:parseOptions reports JSON.parse's SyntaxError text after the fixed prefix; PiG reported Go's. declarations.ts keeps lone surrogates in JSON string escapes (const, property names) and distinguishes an explicit empty signature (renderGlobal: signature !== undefined) from an absent one; PiG replaced the surrogate with U+FFFD and treated "" as absent.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
18f7d391a6(2026-10-05),97051b184f(2026-10-05),b728733998(2026-10-05) - Tests:
codemode/declarations_test.go::TestRenderDeclarationsRendersNamespacedGlobalsAndExplicitSignatures,codemode/upstream_oracle_test.go::TestParseCodemodeSourceMatchesPi,codemode/upstream_oracle_test.go::TestRenderDeclarationsMatchesPi,codemode/upstream_oracle_test.go::TestSchemaToTypeMatchesPi,codemode/upstream_oracle_test.go::TestToCodemodeIdentifierMatchesPi,codemode/upstream_oracle_test.go::TestToolSignatureAndSampleMatchPi,coding/extension/system_prompt_options_wire_test.go::TestBuildSystemPromptOptionsMarshalsCollectionComplete,coding/tool_definition_output_schema_test.go::TestToolDefinitionCarriesOutputSchemaOfBuiltinToolsand 2 more in docs/bug-ledger.json - Release note: Codemode
@optionserrors report V8's JSON error text, tool and schema declarations keep unpaired surrogates, and an explicitly empty global signature renders as Pi renders it.
BF-1971aae
A bash shell that starts at the moment pig handles an emergency exit, a crash or a signal is now killed…
- Source: PiG's own defect
- Symptom: A
bashshell that starts at the momentpighandles an emergency exit, a crash or a signal is now killed with the others. - Root cause: A shell that had started but was not yet tracked could be missed and outlive
pig. - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1971aaebd8(2026-10-06) - Tests:
durable/env/node/watch_link_alias_test.go::TestNodeFileWatcherReportsALinkedTargetForAnEventNamedByTheResolvedFile,internal/codingagent/context_files_oracle_test.go::TestLoadProjectContextFilesMatchesPi,internal/codingagent/emergency_exit_children_test.go::TestEmergencyTerminalExitHelper,internal/codingagent/session_manager_test.go::TestDefaultSessionDirUsesAgentDirOverride,internal/codingagent/session_path_parity_test.go::TestDefaultSessionDirUsesUpstreamEncoding,internal/codingagent/tools/shell_tracked_test.go::TestKillTrackedDetachedChildrenKillsAShellStartedButNotYetTracked - Release note: A
bashshell that starts at the momentpighandles an emergency exit, a crash or a signal is now killed with the others. A shell that had started but was not yet tracked could be missed and outlivepig.
BF-19a07f0
fix(sdk-rs): give the request-start wire test's RequestMsg the batch, child_session, tool_call_index and…
- Source: PiG's own defect
- Symptom: fix(sdk-rs): give the request-start wire test's RequestMsg the batch, child_session, tool_call_index and args_raw fields
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
19a07f0266(2026-10-21) - Tests: None recorded.
BF-1a855cd
The /session block now follows a later theme change and shows the cache-warming countdown as of each…
- Source: PiG's own defect
- Symptom: The
/sessionblock now follows a later theme change and shows the cache-warming countdown as of each redraw, as in Pi. - Root cause: handleSessionCommand snapshots the statistics and adds new ThemedText(renderInfo, 1, 0), so the text is rebuilt on every render with the active theme and the cache-warming status as of that render. PiG built a static string once.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1a855cde32(2026-10-13) - Tests:
internal/codingagent/builtin_tool_cards_oracle_test.go::TestBuiltinToolCardsMatchPi,internal/codingagent/codemode_cards_oracle_test.go::TestCodemodeCardsMatchPi,internal/codingagent/session_info_themed_test.go::TestSessionCommandBlockFollowsThemeChanges,internal/codingagent/tool_codemode_timer_test.go::TestCodemodeRowShowsElapsedWhileRunningAndTookOnceFinished,tui/editor_status_test.go::TestEmbeddedCompactionStatusTakesTheEditorBorderColorLikeWorking,tui/terminal_image_test.go::TestHerdrWithGraphicsSignalRendersKittyImage - Release note: The
/sessionblock now follows a later theme change and shows the cache-warming countdown as of each redraw, as in Pi. It kept the colors and the status text from the moment the command ran.
BF-1b5540a
client.Client.Disconnect takes the reason string as Pi's Client.disconnect(reason) does (it was an…
- Source: PiG's own defect
- Symptom:
client.Client.Disconnecttakes the reason string as Pi'sClient.disconnect(reason)does (it was anerror); the connection fails with aDisconnectedErrorcarrying it. - Root cause: Client.disconnect(reason = "Client disconnected") accepts a string and Connection.disconnect wraps a string in a DisconnectedError; Go's Client.Disconnect took an error, so a Radius reconnect client (Disconnect(string)) could not be the real Client. Tests: the disconnect-reason cases now assert the DisconnectedError message; a mutant that ignores the reason fails TestClientConnectionLifecycleAndStaleTransport and TestClientRequestPreconditionsAndDisposal.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1b5540aec7(2026-10-07) - Tests:
durable/storage/jsonl/jsonl_storage_reads_test.go::TestJsonlStorageReadsCommittedRecordsRecoversThemAndFailsAfterClose,durable/tools/bash_input_test.go::TestBashToolInputIsTheBashSchemaArguments - Release note:
client.Client.Disconnecttakes the reason string as Pi'sClient.disconnect(reason)does (it was anerror); the connection fails with aDisconnectedErrorcarrying it.
BF-1c6b864
A Session that an extension creates with createAgentSession, or that coding.CreateAgentSession returns…
- Source: PiG's own defect
- Symptom: A Session that an extension creates with
createAgentSession, or thatcoding.CreateAgentSessionreturns, now reportsentry_appendedto its listeners when an extension that its resource loader loaded callspi.appendEntry, as Pi does. - Root cause: A Session whose resource loader started the extensions (a child session an extension creates, or coding.CreateAgentSession) wrote the entry that pi.appendEntry appended to the log and told no listener. Pi's appendEntry action also emits entry_appended (agent-session.ts:3321-3327), before the event the extension was handling reaches the listeners; ctx.sessionManager. appendCustomEntry emits none. The loader host's appendEntry action now emits the event the same way every mode's action does.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
1c6b8643ab(2026-10-25) - Tests: None recorded.
- Release note: A Session that an extension creates with
createAgentSession, or thatcoding.CreateAgentSessionreturns, now reportsentry_appendedto its listeners when an extension that its resource loader loaded callspi.appendEntry, as Pi does. The event arrives before the event the extension was handling, andctx.sessionManager.appendCustomEntrystill writes the log without an event. The Session wrote the entry and told no listener.
BF-1ca0e2f
fix(codingagent): the terminal reader yields to the main loop it readied before it polls again
- Source: PiG's own defect
- Symptom: fix(codingagent): the terminal reader yields to the main loop it readied before it polls again
- Root cause: The reader readies the main loop on its own P and then blocks in a poll system call that keeps the P, so the main loop waited for another thread to steal it.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
1ca0e2facc(2026-10-13) - Tests: None recorded.
BF-1cc7519
fix(sdk): a command, shortcut, flag or renderer registered after load reaches the host in every SDK
- Source: PiG's own defect
- Symptom: fix(sdk): a command, shortcut, flag or renderer registered after load reaches the host in every SDK
- Root cause: Pi's registerCommand/registerShortcut/registerFlag/registerMessageRenderer/ registerEntryRenderer write the extension's own Map and every runner getter reads it live, so a registration from a handler takes effect. Node, Go and Python kept it in a local map and dropped it (probed: the late-registrations row failed with the late entries missing). Each SDK now sends registerDeclaration after connect (Go and Python under the registration lock, Node synchronously like registerTool); Rust, whose Extension is consumed by run, gains Context::register_command/_shortcut/_flag/ _message_renderer/_entry_renderer backed by per-connection late tables the dispatcher consults first. Go SDK lookups take…
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
1cc75198f6(2026-10-10) - Tests: None recorded.
BF-1ce00b0
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: Cache schema pattern regexps (typebox.CompilePattern) for typebox and chord objectFits; read Description members from their own JSON text instead of a re-encode; encode nested result values in place; one wire marshal in WorkerToHostMessage; cap the entered dump-buffer pool; no Sprintf in the /session cost loop. Proven cold sites carry //perf:ok.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
1ce00b0a53(2026-10-13) - Tests: None recorded.
BF-1d02c73
Filters that Pi writes with toLowerCase() (select lists, the config selector, file autocomplete, /model…
- Source: PiG's own defect
- Symptom: Filters that Pi writes with
toLowerCase()(select lists, the config selector, file autocomplete,/modelsearch, minimatchnocase) lowercase as JavaScript does (İbecomesi̇, a final sigmaς), not as Go'sstrings.ToLower. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
68a4ed2b54(2026-10-09),1d02c73a90(2026-10-10) - Tests:
internal/codingagent/fullscreen_modal_viewport_test.go::TestFullscreenSelectionKeepsItsPageKeys,internal/codingagent/fullscreen_modal_viewport_test.go::TestRegularModeSelectorPageKeyMovesItsSelection,internal/codingagent/llama/ui_test.go::TestHuggingFaceSearchPageKeysMoveTenResults,internal/codingagent/modal_viewport_input_test.go::TestEditorSlotSelectorPageKeyMovesSelectionInEveryMode,internal/codingagent/select_page_keys_test.go::TestCustomEditorGivesSelectionPageKeysToAnOpenAutocomplete,internal/codingagent/select_page_keys_test.go::TestFirstTimeSetupPageKeysMoveToTheEnds,internal/codingagent/select_page_keys_test.go::TestSessionSelectorCapturesSelectionPageKeys,internal/codingagent/select_page_keys_test.go::TestTrustSelectorPageKeysMoveToTheEndsand 9 more in docs/bug-ledger.json - Release note: Filters that Pi writes with
toLowerCase()(select lists, the config selector, file autocomplete,/modelsearch, minimatchnocase) lowercase as JavaScript does (İbecomesi̇, a final sigmaς), not as Go'sstrings.ToLower.
BF-1d1c8aa
RPC mode now ends by the signal when a termination signal arrives while a quit session_shutdown handler is…
- Source: PiG's own defect
- Symptom: RPC mode now ends by the signal when a termination signal arrives while a quit
session_shutdownhandler is pending. - Root cause: RPC mode exited 0 instead of by SIGTERM when the extension processes PiG stops before dying completed a pending quit dispose or reported a drained loop; no exit starts once the signal's default action begins. The core-skills fixture used skill.Path (Pi: filePath). The theme border test resolved the theme in the terminal's color mode, where 256-color collapses two levels as in Pi. The MCP trust test expanded Windows 8.3 short names that the process never sees. go fix applied; pig-go.json regenerated.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1d1c8aa106(2026-10-06) - Tests:
ai/union_constants_upstream_test.go::TestUpstreamStringUnionsMatchTheGoConstants,ai/utils_exports_upstream_test.go::TestGetOverflowPatternsMatchesUpstreamList,tui/theme_attribute_helpers_test.go::TestThemeBorderColorFunctions - Release note: RPC mode now ends by the signal when a termination signal arrives while a quit
session_shutdownhandler is pending. The extension processes PiG stops first could complete the pending shutdown or report a drained event loop, and the process exited 0 instead of by the signal.
BF-1d5b272
A text file passed as an @file argument that holds invalid UTF-8 now reaches the prompt as Pi sends it…
- Source: PiG's own defect
- Symptom: A text file passed as an
@fileargument that holds invalid UTF-8 now reaches the prompt as Pi sends it: each ill-formed byte sequence becomes one U+FFFD replacement character. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1d5b272feb(2026-10-09),433d232815(2026-10-09) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunkand 3 more in docs/bug-ledger.json - Release note: A text file passed as an
@fileargument that holds invalid UTF-8 now reaches the prompt as Pi sends it: each ill-formed byte sequence becomes one U+FFFD replacement character. Pig previously copied the raw bytes, which later became one replacement character per byte.
BF-1dcaef7
Durable: a JSONL line that starts with a byte order mark now opens, as in Pi, instead of failing with…
- Source: PiG's own defect
- Symptom: Durable: a JSONL line that starts with a byte order mark now opens, as in Pi, instead of failing with "Malformed complete ... line N".
- Root cause: Lineage through a missing parent conversation now fails as in Pi. Pi's findLatestHeadMarker, visibleEntries and visibleEntriesAscending (memory.ts:509, :695, :717) read
.parentof a conversation that is not stored and throw "Cannot read properties of undefined (reading 'parent')"; commit does not check that a parent exists (memory.ts:266-287), so a caller or a dropped commit leaves one dangling. Pig stopped at the missing record and returned a truncated history (scanEntries even returned items). Each walk now returns that error at the same point, after the entries Pi yields first. The recovery differential no longer overwrites Pig's answer with Pi's TypeError; danglingParentPrograms pins… - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1dcaef738b(2026-10-10),f71185dc7a(2026-10-10) - Tests:
durable/storage/jsonl/recovery_rewrite_test.go::TestJsonlRecoveryIgnoresALeadingByteOrderMarkOnEachLine,durable/storage/jsonl/recovery_rewrite_test.go::TestJsonlRecoveryRewritesReclaimedSidecarRecordsCanonically,durable/storage/jsonl_pi_oracle_test.go::TestJsonlStorageMatchesPiOnSeededPrograms,durable/storage/jsonl_recovery_pi_oracle_test.go::TestJsonlRecoveryMatchesPiOnDamagedDirectories,durable/storage/memory_dangling_parent_test.go::TestMemoryStorageLineageThroughAMissingParentConversationFails,durable/storage/memory_pi_oracle_test.go::TestMemoryStorageMatchesPiOnSeededPrograms,durable/storage/memory_status_test.go::TestMemoryStorageRejectsAnUnknownTaskOrSubmissionStatus - Release note: Durable: a JSONL line that starts with a byte order mark now opens, as in Pi, instead of failing with "Malformed complete ... line N". When recovery reclaims a sidecar file it writes the records it keeps in canonical JSON, as Pi does, instead of copying their original bytes.
BF-1ed43b3
fix(train): load pig_sdk.tui with the package, run the TS queue check with no runtime, pin three fixture…
- Source: PiG's own defect
- Symptom: fix(train): load pig_sdk.tui with the package, run the TS queue check with no runtime, pin three fixture modules to the root SDK version
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
1ed43b3d96(2026-10-21) - Tests: None recorded.
BF-1ee7dea
The Mistral request URL for a base URL with a non-special scheme or a host or port the WHATWG parser rejects…
- Source: PiG's own defect
- Symptom: The Mistral request URL for a base URL with a non-special scheme or a host or port the WHATWG parser rejects (for example a port above 65535) follows Pi's
new URL: it fails where Pi throws and keeps the base path where Pi does. - Root cause: The URL expression of requestMistralStream (mistral-conversations.ts:303-305: new URL(baseUrl); pathname set to the trimmed path plus '/'; new URL('v1/chat/completions', baseUrl)) runs in Node (testdata/mistral_url.mjs) on 4514 seeded base URLs. It is now ParseURL, the pathname setter (WithPathname) and ResolveRelativePath; the hand-rolled authority normalizer and resolveDirectoryPath are deleted. The file drive-letter rule cannot trigger here because the directory path always ends in '/'.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),0b4e1e3376(2026-10-09),15a5c0b670(2026-10-09),1ee7dea3e4(2026-10-09),23aa78b173(2026-10-09),66b0fc0539(2026-10-09),7c4d874438(2026-10-09),a739b6dcf2(2026-10-09),c4d51d5c1a(2026-10-09),d80c1cd150(2026-10-09),da8a8704c6(2026-10-09) - Tests:
ai/anthropic_token_endpoint_oracle_test.go::TestAnthropicTokenEndpointRuleMatchesSDK,ai/bedrock_region_oracle_test.go::TestBedrockEndpointRegionMatchesPi,ai/chatgpt_manual_input_oracle_test.go::TestChatGPTManualInputMatchesPiOnSeededProbes,ai/codex_account_id_oracle_test.go::TestCodexAccountIDMatchesPi,ai/codex_vertex_urls_oracle_test.go::TestCodexWebSocketAndVertexVersionURLsMatchPi,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,ai/mistral_url_oracle_test.go::TestMistralChatCompletionsURLMatchesPiOnSeededProbes,ai/trusted_url_oracle_test.go::TestTrustedVerificationURLsMatchPiand 2 more in docs/bug-ledger.json - Release note: The Mistral request URL for a base URL with a non-special scheme or a host or port the WHATWG parser rejects (for example a port above 65535) follows Pi's
new URL: it fails where Pi throws and keeps the base path where Pi does.
BF-1f7fa2f
In print and JSON mode, an extension in its own Node process no longer runs its callbacks with a stale ctx…
- Source: PiG's own defect
- Symptom: In print and JSON mode, an extension in its own Node process no longer runs its callbacks with a stale ctx when a later extension's handler awaits a timer or I/O.
- Root cause: Pi's runner awaits each extension's handler in turn on one event loop (runner.ts:1089-1116). When a later extension's session_shutdown or agent_settled handler awaits a timer or I/O, that loop runs the immediate or timer an earlier extension's handler queued, with a live ctx, before print mode invalidates the runner (agent-session-runtime.ts:404-410, agent-session.ts:1393-1406). With isolated Node processes the earlier process waited for the host's next step after answering (), so its callbacks ran only after invalidate, or never, because the host stopped the process first. Packed processes share one loop and matched Pi.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
311609427b(2026-10-07),382156cdb7(2026-10-07),49798a80d2(2026-10-07),8723a419d7(2026-10-07),8d990c5d54(2026-10-07),a79de03e7f(2026-10-07),1f7fa2fe22(2026-10-09) - Tests:
coding/cli/resource_loader_gaps_upstream_test.go::TestPrintModeInvalidDiscoveredFileURLFailsBind,coding/extension/host/inproc/upstream_scoped_models_test.go::TestContextThinkingLevelReadsTheBoundRuntimeLive,coding/extension/host/subprocess/runtime_node_loop_turn_test.go::TestNodeLoopTurnRunsQueuedImmediatesBeforeLaterFrames,coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodePromptEndWaitEndsWhenAnotherProcessTurnsTheLoop,coding/extension/widget_placement_test.go::TestPowerShellToolInputIsTheBashInputShape,coding/mcpext/extension_test.go::TestAgentSessionMCPHoldsTheFirstPromptForServersWithDirectToolsOnlyUpToTheStartupWait,coding/session_extend_resources_test.go::TestSessionExtendsItsLoaderAfterExtensionReplacement,coding/session_extend_resources_test.go::TestSessionExtendsItsLoaderFromResourcesDiscoverand 10 more in docs/bug-ledger.json - Release note: In print and JSON mode, an extension in its own Node process no longer runs its callbacks with a stale ctx when a later extension's handler awaits a timer or I/O. Pi runs every extension on one event loop, so the immediate or timer that one extension's
session_shutdownoragent_settledhandler queues runs while the next extension's handler waits, before the runner is invalidated. The Node runtime now reports such a wait with aloop_turnednotification, which the host forwards to every other Node process as its next step. Packed extensions already shared one loop and behave as before; RPC and interactive mode are unchanged.
BF-1fbdabb
A CRLF that a proxy splits across two network chunks no longer aborts an Anthropic turn with `Could not parse…
- Source: A Pi defect that PiG fixed first (divergence D121)
- Symptom: A CRLF that a proxy splits across two network chunks no longer aborts an Anthropic turn with
Could not parse Anthropic SSE event message_start(D121, pi-mono #10390). - Root cause: Pi anthropic-messages.ts:454-466 consumeLine ends the line at a trailing CR, so the LF of a split CRLF flushes the record without data and the turn aborts. Wait for the next chunk.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10390
- Removed when Pi fixes it: Pi holds back a trailing
\rinconsumeLine. - Fix commits:
1fbdabb354(2026-10-09),40bc651bae(2026-10-10),c3558fbe52(2026-10-12) - Tests:
ai/anthropic_sse_crlf_split_test.go::TestAnthropicStreamSurvivesCRLFSplitAcrossChunks,ai/anthropic_stream_test.go::TestAnthropicSSEReaderLineDecoding - Release note: A CRLF that a proxy splits across two network chunks no longer aborts an Anthropic turn with
Could not parse Anthropic SSE event message_start(D121, pi-mono #10390).
BF-1fcf017
toUpperCase ports use a shared JavaScript jsstring.ToUpper (matches Node over every code point).…
- Source: PiG's own defect
- Symptom:
toUpperCaseports use a shared JavaScriptjsstring.ToUpper(matches Node over every code point). minimatchnocasefoldsᾀandßas JavaScript does. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1d02c73a90(2026-10-10),1fcf01772a(2026-10-10),79b035f49f(2026-10-10) - Tests:
durable/core/history/js_space_agreement_test.go::TestIsJSSpaceAgreesWithJsstringIsSpace,internal/chord/chordjson/own_keys_index_bound_test.go::TestOwnKeysArrayIndexStopsBelowTwoToTheThirtyTwoMinusOne,internal/jsstring/upper_oracle_test.go::TestToUpperMatchesNode,internal/minimatch/canonicalize_upper_test.go::TestCanonicalizeUsesJavaScriptUpperCase,tui/select_page_keys_test.go::TestSelectListFilterLowercasesAsJavaScriptDoes,tui/widthx/js_space_agreement_test.go::TestIsJSSpaceAgreesWithJsstringIsSpace - Release note:
toUpperCaseports use a shared JavaScriptjsstring.ToUpper(matches Node over every code point). minimatchnocasefoldsᾀandßas JavaScript does.
BF-202b8f1
On arm64 and other CPUs with a fused multiply-add, the OKHSL saturation of a color (used when generating the…
- Source: PiG's own defect
- Symptom: It differed from Pi's in the last bit for about 9% of sRGB colors.
- Root cause: rgbToOkhsl assigns k1 := 0.8 c0 and adds it in k1 + (1 - k1/cMid) chroma. Assigning a product does not round it in Go: on arm64 the compiler fused 0.8 * c0 into that addition (FMADD), so ColorToOkhsl's saturation, which the system theme generator reads, differed from Pi's oklab.ts:225-226 in the last bit for 1523533 of the 16.7M sRGB colors (633339 of those then match Pi exactly; the others differ from Pi through Go's Pow/Cbrt/Atan2 as well).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
202b8f1943(2026-10-13),7633906d03(2026-10-13),a00dbe6901(2026-10-13),bd3e8ab24b(2026-10-13) - Tests:
ai/device_code_poll_oracle_test.go::TestDeviceCodePollDeadlineRoundsTheProductLikePi,ai/oauth_expiry_arithmetic_test.go::TestBuiltinOAuthExpiryUsesJavaScriptArithmetic,ai/openai_codex_http_error_test.go::TestCodexHTTPErrorResetMinutesRoundTheProductFirst,internal/codingagent/cache_warmer_test.go::TestCacheWarmingExpectedSavingsRoundsTheProductLikePi,internal/codingagent/pig_egg3d_test.go::TestPig3dFlightHomeOffsetsRoundLikePi,internal/codingagent/radius_login_oracle_test.go::TestRadiusLoginMenuRendersLikePi,internal/jsmath/log_oracle_test.go::TestLogMatchesNode,tui/colors_test.go::TestColorToOkhslRoundsTheChromaStopProduct - Release note: On arm64 and other CPUs with a fused multiply-add, the OKHSL saturation of a color (used when generating the system theme's colors) rounds
0.8 * c0before adding it, as Pi does. Before, it differed from Pi's in the last bit for about 9% of sRGB colors.
BF-205933e
fix(ai): a Gemini model outside the catalog carries the provider's API, so replay's isSameModel matches Pi…
- Source: PiG's own defect
- Symptom: fix(ai): a Gemini model outside the catalog carries the provider's API, so replay's isSameModel matches Pi (providers-registry/04 googleToolIds and googleThinkingSig were red since the catalog dropped gemini-3-pro: the fallback Model had no api, which made an assistant message without one the same model and one with the provider's api another model).
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
205933ee87(2026-10-10) - Tests:
ai/google_uncataloged_model_test.go::TestGoogleUncatalogedModelHasTheProvidersAPIForReplay
BF-20718f7
fix(coding-agent): resolve model references through one provider selection policy (Pi)
- Source: PiG's own defect
- Symptom: fix(coding-agent): resolve model references through one provider selection policy (Pi)
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
20718f71ec(2026-10-14) - Tests:
coding/cli/credential_print_test.go::TestCredentialPrintModelWithoutProviderUsesCatalogOfConfiguredProviders,internal/codingagent/model_reference_upstream_test.go::TestResolveCliModelDuplicateRawIDsMatchesUpstream,internal/codingagent/model_reference_upstream_test.go::TestResolveModelReferenceMatchesUpstream,internal/codingagent/model_reference_upstream_test.go::TestResolveModelScopeAmbiguousProvidersMatchesUpstream
BF-20aee29
The crash notice prints the year of a crash time the way Node's toLocaleString does: unpadded, and counted…
- Source: PiG's own defect
- Symptom: The crash notice prints the year of a crash time the way Node's toLocaleString does: unpadded, and counted back from 1 for years before 1 CE.
- Root cause: interactive-mode.ts:1222 prints
new Date(crash.timestamp).toLocaleString(). The Go notice used the layout "1/2/2006, 3:04:05 PM", whose year is zero padded to four digits and signed before 1 CE; en-US toLocaleString prints the Gregorian era year unpadded and, before 1 CE, counted back from 1 without a sign ("4/15/151", "10/13/1721" for 1721 BCE). enUSDateTime now builds the string. A crash record's year is the current one in practice, so this shows only for a forged or damaged record. The locale stays en-US: Node uses the process locale (LANG=de_DE gives "31.12.1969, 16:00:00"), which is not modelled and not recorded as a divergence pending the owner. - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
20aee29744(2026-10-11) - Tests:
internal/codingagent/crash_notice_date_oracle_test.go::TestCrashNoticeTimeMatchesToLocaleString - Release note: The crash notice prints the year of a crash time the way Node's toLocaleString does: unpadded, and counted back from 1 for years before 1 CE.
BF-211af49
/hotkeys lists extension shortcuts, and the extension shortcut listener tries them, in the order of Pi's…
- Source: PiG's own defect
- Symptom:
/hotkeyslists extension shortcuts, and the extension shortcut listener tries them, in the order of Pi'sgetShortcutsMap (load order, registration order, a re-registered key keeps its first position) instead of path then key / Go map order (coding/extension/host/inproc/runner.goOrderedShortcuts,internal/codingagent/interactive_hotkeys.go,interactive_helpers.go;TestRunnerShortcutsMatchPinow compares Map order, regressionTestHotkeysCommandListsExtensionShortcutsInRegistrationOrder). - Root cause: Runner.OrderedShortcuts returns getShortcuts' entries in Map iteration order (first-registration position, last value); Shortcuts is the map view. /hotkeys sorted by extension path then key and the listener ranged a Go map, so Pi's registration order and first-match-wins for overlapping spellings of one key were lost.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),211af490e0(2026-10-11),4ae159f3cd(2026-10-11),a2755058da(2026-10-11),c4ad739eb8(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/discover_extensions_oracle_test.go::TestDiscoverExtensionPathsMatchPi,coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,internal/codingagent/armin_test.go::TestArminFramesMatchPinnedPiand 13 more in docs/bug-ledger.json - Release note:
/hotkeyslists extension shortcuts, and the extension shortcut listener tries them, in the order of Pi'sgetShortcutsMap (load order, registration order, a re-registered key keeps its first position) instead of path then key / Go map order (coding/extension/host/inproc/runner.goOrderedShortcuts,internal/codingagent/interactive_hotkeys.go,interactive_helpers.go;TestRunnerShortcutsMatchPinow compares Map order, regressionTestHotkeysCommandListsExtensionShortcutsInRegistrationOrder).
BF-2165463
On Windows, saving credentials to auth.json, models.json or the MCP OAuth store no longer fails at once…
- Source: PiG's own defect
- Symptom: On Windows, saving credentials to
auth.json,models.jsonor the MCP OAuth store no longer fails at once withEPERM: operation not permitted, mkdir '….lock'when another process has just released the lock and Windows still holds the directory pending deletion. - Root cause: Not recorded.
- User impact: Credential or permission exposure
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
21654635e7(2026-10-13) - Tests:
internal/codingagent/config_schemas_runtime_upstream_test.go::TestConfigSchemasAcceptCustomCompatibilitySettingsAndRejectMalformedKnownFields,internal/codingagent/config_schemas_runtime_upstream_test.go::TestConfigSchemasAcceptSchemaInKeybindingsJSONWithoutTreatingItAsAnAction,internal/codingagent/config_schemas_runtime_upstream_test.go::TestConfigSchemasAcceptSchemaInSettingsJSON,internal/codingagent/config_schemas_runtime_upstream_test.go::TestConfigSchemasValidateSchemaInModelsJSONAtRuntime,internal/pilock/delete_pending_windows_test.go::TestAcquireRetriesLockBeingRemovedAtMkdirThenSurfacesIt,internal/pilock/delete_pending_windows_test.go::TestAcquireSurfacesLockBeingRemovedAtStatThatIsStaleOrALink,internal/pilock/delete_pending_windows_test.go::TestAuthAcquireRetriesLockBeingRemovedAtStatThatIsStaleOrALink,internal/pilock/delete_pending_windows_test.go::TestAuthAcquireWaitsOutLockBeingRemovedAtMkdirand 4 more in docs/bug-ledger.json - Release note: On Windows, saving credentials to
auth.json,models.jsonor the MCP OAuth store no longer fails at once withEPERM: operation not permitted, mkdir '….lock'when another process has just released the lock and Windows still holds the directory pending deletion. As in Pi 1.2.0, the lock is retried: the synchronous path within its ten attempts, the cancellable path up to five times. Settings and trust locks still fail on the firstEPERM, as in Pi.
BF-224dbd9
On Linux and macOS, the bash tool, a ! command and an extension's exec no longer report no output for a…
- Source: PiG's own defect
- Symptom: On Linux and macOS, the bash tool, a
!command and an extension'sexecno longer report no output for a command that printed, when the machine is too busy to run PiG's pipe reader within 100 ms of the command's exit. - Root cause: childwait.Wait ends the post-exit wait when the 100 ms grace of Pi's waitForChildProcess elapses and mayHoldOutput says a descendant may hold the pipe, which on Unix is always true (a process that left the group can). The grace measures a quiet writer, but the reader that was to take the output had not run for that long on a busy machine, so what the shell wrote before it exited was still unread in the pipe and Wait closed it and dropped it: the bash tool, user bash and an extension's exec reported no output for a command that printed. A reader that had taken a chunk but not yet handed it over lost it the same way when the grace released a quiet held pipe in that gap.
- User impact: Lost or altered data
- Platform: macOS, Linux
- Fixed in: Unreleased
- Fix commits:
224dbd923f(2026-10-24) - Tests:
internal/childwait/pipe_blocked_unix_test.go::TestPollPipeAnswersWhileAReaderIsBlockedInReadingIt,internal/childwait/pipe_unix_test.go::TestPipesWaitDeliversEveryPipeWhenOneEndedAndAHolderKeepsTheOther,internal/childwait/pipe_unix_test.go::TestPipesWaitDeliversWhatWaitsUnreadInAPipeAHolderKeepsOpen,internal/childwait/pipe_unix_test.go::TestPipesWaitReadsToEOFAfterTheGraceWhenEveryWriteEndIsClosed,internal/childwait/pipe_unix_test.go::TestPollPipeReportsTheWriteEndsAndTheBytesOfAPipe,internal/childwait/pipe_unix_test.go::TestPollPipeTakesNothingAndLeavesTheReaderWorking,internal/childwait/wait_test.go::TestPipesWaitDeliversAChunkAReaderTookBeforeTheGraceReleasedTheHeldPipe,internal/childwait/wait_test.go::TestWaitIdleArmsTheGraceAgainWhileAPipeHoldsUnreadOutputand 3 more in docs/bug-ledger.json - Release note: On Linux and macOS, the bash tool, a
!command and an extension'sexecno longer report no output for a command that printed, when the machine is too busy to run PiG's pipe reader within 100 ms of the command's exit. PiG ended the wait for the output pipe after Pi's 100 ms post-exit grace even though the command had written its output before it exited and the reader had not yet taken it, so the output was lost; on a loaded machineecho answercame back as(no output). PiG now asks the pipe itself when the grace elapses: a pipe that no process holds is read to its end, a pipe that holds output nobody has read yet keeps the wait going, and only a pipe that a background process still holds and that is quiet is released after the grace, as in Pi.
BF-2280bf8
The per-API stream entry points (ai.AnthropicMessagesAPI() through ai.PiMessagesAPI() and the deprecated…
- Source: PiG's own defect
- Symptom: The per-API stream entry points (
ai.AnthropicMessagesAPI()throughai.PiMessagesAPI()and the deprecatedai.StreamX/ai.StreamSimpleXaliases) end a failure inside the API's own stream the way Pi's API module does: the final message carries the module'sapi(its own API for azure-openai-responses, bedrock-converse-stream, google-generative-ai, google-vertex and openai-codex-responses, the model'sapiotherwise), and a request aborted before it starts stops withabortedinstead oferror. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),75c06b6556(2026-10-09),2280bf8320(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPiand 4 more in docs/bug-ledger.json - Release note: The per-API stream entry points (
ai.AnthropicMessagesAPI()throughai.PiMessagesAPI()and the deprecatedai.StreamX/ai.StreamSimpleXaliases) end a failure inside the API's own stream the way Pi's API module does: the final message carries the module'sapi(its own API for azure-openai-responses, bedrock-converse-stream, google-generative-ai, google-vertex and openai-codex-responses, the model'sapiotherwise), and a request aborted before it starts stops withabortedinstead oferror. OnlystreamSimple's missing-key check stays a setup error carrying the model'sapi.
BF-2309604
Tool schemas in OpenAI Completions, OpenAI Responses, Codex and Mistral request bodies keep the members in…
- Source: PiG's own defect
- Symptom: Tool schemas in OpenAI Completions, OpenAI Responses, Codex and Mistral request bodies keep the members in the order the tool declares them.
- Root cause: Pi passes a tool's parameters object through to the body (openai-completions.ts convertTools :1506-1514, openai-responses-shared.ts :386-390), so JSON.stringify writes the schema's members in declaration order; a strict schema is the same object rebuilt member by member (constrained-sampling.ts makeJsonSchemaNodeStrict: required, then additionalProperties are assigned after the existing members, and an optional property becomes {anyOf: [property, {type: "null"}]}). PiG copied ToolSchema.Parameters as a map[string]any and dropped the recorded parameterOrder, so every schema object was written in sorted order.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2309604d80(2026-10-10),c10cde302d(2026-10-10),f29e906cb3(2026-10-10),fe16108cc7(2026-10-10) - Tests:
agent/assistant_member_order_oracle_test.go::TestAssistantLateMembersFollowPiCreationOrder,ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPi,ai/constrained_sampling_oracle_test.go::TestConstrainedSamplingMatchesPi,ai/openai_request_key_order_oracle_test.go::TestOpenAICompletionsRequestKeysFollowPiAssignmentOrder,ai/openai_responses_request_key_order_oracle_test.go::TestOpenAIResponsesRequestKeysFollowPiAssignmentOrder,ai/perf_gate_norace_test.go::TestProviderRequestHistoryAllocationGate,ai/request_body_escaping_test.go::TestProviderRequestBodiesWriteCharactersAsJSONStringifyDoes,coding/cli/rpc_assistant_member_order_test.go::TestRPCAssistantMessageMembersMatchTheSessionLineOrderand 2 more in docs/bug-ledger.json - Release note: Tool schemas in OpenAI Completions, OpenAI Responses, Codex and Mistral request bodies keep the members in the order the tool declares them. Strict schemas add
requiredand thenadditionalProperties, as Pi does.cache_controlcomes after the members of the tool or text part that carries it.
BF-2316fe3
The Piglet JSON Schema that pig piglet schema prints rejected powershell in tools, which `pig piglet…
- Source: PiG's own defect
- Symptom: The Piglet JSON Schema that
pig piglet schemaprints rejectedpowershellintools, whichpig piglet validateaccepts as a built-in tool, and stated nothing for most of the other closed vocabulary. - Root cause: The schema listed seven built-in tools while the parser accepts eight, so it rejected a Piglet that used powershell. An audit of every enum and field found more drift: the agentEnv runtime modes and policy presets, the strip IDs, the name, build target, output name, release version and secret patterns, the required keys of a mount, and the exactly-one agentEnv form were all unstated.
- User impact: Credential or permission exposure
- Platform: Windows
- Fixed in: 0.5.3
- Fix commits:
2316fe38fb(2026-10-22) - Tests:
coding/piglet/internal/genstripids/main_test.go::TestCommittedSchemaStripEnumsMatchRegistrations,coding/piglet/schema_test.go::TestAC1SchemaParserAgreement,coding/piglet/schema_vocabulary_test.go::TestSchemaAcceptsEveryPigletTheRepositoryShips,coding/piglet/schema_vocabulary_test.go::TestSchemaAgreesWithParserOnEveryValueOfASet,coding/piglet/schema_vocabulary_test.go::TestSchemaEnumsMatchParserSets,coding/piglet/schema_vocabulary_test.go::TestSchemaFieldsMatchParserFields,coding/piglet/schema_vocabulary_test.go::TestSchemaNeverRejectsWhatTheParserAccepts - Release note: The Piglet JSON Schema that
pig piglet schemaprints rejectedpowershellintools, whichpig piglet validateaccepts as a built-in tool, and stated nothing for most of the other closed vocabulary. It now lists all eight built-in tools, the strip IDs of each strip list, theagentEnvruntime modes (inject,image) and policy presets (minimal,standard,elevated), and carries the parser's name, build target, output name, release version and secret patterns, the requiredsourceandtargetof a mount, and the rule thatagentEnvnames exactly one ofimage,devContainerandsource.make piglet-strip-idswrites the strip ID enums, and tests that read the parser's sets from code fail when the schema and the parser disagree.
BF-234316d
On Windows, a Go extension's source hash no longer fails, or makes a build beside it fail, with `The process…
- Source: PiG's own defect
- Symptom: On Windows, a Go extension's source hash no longer fails, or makes a build beside it fail, with
The process cannot access the file because it is being used by another process. - Root cause: both source hash walks (subprocess walkDirSources and runtimecell hashGoModuleDir) listed directories with os.ReadDir, whose open on Windows shares read and write but not delete. A handle without FILE_SHARE_DELETE has two effects on a build running beside the hash, which empties and fills the same trees: any delete or rename of the directory while the walk has it open fails with a sharing violation, and a directory that another process holds open for delete (os.RemoveAll does, for an instant) makes the walk's own open fail with one. A throwaway Windows run of the untagged churn tests failed 100 of 100 and 96 of 100 on that ("open ...\rmeta27: The process cannot access the file because it is…
- User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: Unreleased
- Fix commits:
234316d435(2026-10-24) - Tests:
coding/extension/host/runtimecell/hash_acl_windows_test.go::TestHashGoModuleFailsClosedOnALiveACLDeniedDirectory,coding/extension/host/runtimecell/readdir_shared_test.go::TestWalkDirSharedMatchesFilepathWalkDir,coding/extension/host/runtimecell/readdir_shared_test.go::TestWalkDirSharedReportsListingErrorsToFn,coding/extension/host/runtimecell/vanished_dir_test.go::TestHashGoModuleIgnoresConcurrentChurnOfUntaggedDirectories,coding/extension/host/subprocess/hash_acl_windows_test.go::TestHashSourceDirFailsClosedOnALiveACLDeniedDirectory,coding/extension/host/subprocess/hash_build_output_test.go::TestHashSourceDirIgnoresConcurrentChurnInReplacedModule - Release note: On Windows, a Go extension's source hash no longer fails, or makes a build beside it fail, with
The process cannot access the file because it is being used by another process. The hash listed every directory of the extension and of the modules itsgo.modreplaces through a handle that did not share delete, so a build that deleted or renamed a directory in those trees while the hash had it open (Cargo and rustc remove temp directories under a replaced repository root) failed with a sharing violation, and the hash failed in turn when it opened a directory that build was deleting. The hash now lists directories with every share mode. A directory that is there and denies the listing (an ACL deny) still fails the hash.
BF-24872ab
Extensions receive each model (ctx.model, model_select, ctx.modelRegistry reads, scoped models…
- Source: PiG's own defect
- Symptom: Extensions receive each model (
ctx.model,model_select,ctx.modelRegistryreads, scoped models, virtual-model routes and provider-object calls) as Pi's model object, with its members in Pi's order, instead of sorted by member name withnullfor members the model does not set. - Root cause: ctx.model, model_select, ctx.modelRegistry (find/getAll/typed models, getAvailableOfType), scoped models, virtual-model routes and provider-object calls carried extension.ModelInfo: a map[string]any that json.Marshal sorted, with null for absent members and PiG's aliases (modelId, displayName, maxOutputTokens, *CostPer1M). Pi hands extensions the runtime's model object itself (runner.ts:609): its catalog record's member order for a built-in or stored model, provider-composer.ts:230-248's for a composed one.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
24872ab41e(2026-10-13),48a28fe06a(2026-10-13) - Tests:
coding/cli/model_projection_test.go::TestModelObjectPreservesPiModelShape,coding/extension/host/subprocess/native_provider_models_test.go::TestNativeProviderModelsKeepTheirMemberOrder,coding/extension/model_object_oracle_test.go::TestModelObjectsMatchPisModelObjects,coding/extension/model_object_test.go::TestMarshalModelObjects,coding/extension/model_object_test.go::TestModelObjectDefaults,coding/extension/model_object_test.go::TestModelObjectKeepsARecordsEmptyTiers,coding/extension/model_object_test.go::TestModelObjectOfAComposedModelHasPisMembersInPisOrder,coding/extension/model_object_test.go::TestModelObjectOfImageAndClassifierModels - Release note: Extensions receive each model (
ctx.model,model_select,ctx.modelRegistryreads, scoped models, virtual-model routes and provider-object calls) as Pi's model object, with its members in Pi's order, instead of sorted by member name withnullfor members the model does not set.
BF-24a821d
The openai stream decoder follows openai 7.19.0, the version packages/ai declares: the stream ends at [DONE]…
- Source: PiG's own defect
- Symptom: The openai stream decoder follows openai 7.19.0, the version packages/ai declares: the stream ends at [DONE] without waiting for the connection to close, the last event is delivered when the server omits its blank line, [DONE] must be the whole data, and an event named error throws an APIError.
- Root cause: Differential: 6000 seeded response bodies (every line ending, comments, fields with and without the optional space, repeated data, [DONE] and [DONE] tails, JSON errors, malformed JSON, BOM, invalid UTF-8, unterminated last event, split at arbitrary bytes) through the pinned openai 7.19.0 Stream.fromSSEResponse and Pig's decoder: items, API error messages and malformed-JSON errors compared. 631 of 6000 differed.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
24a821d908(2026-10-10) - Tests:
ai/openai_stream_decoder_test.go::TestOpenAIStreamDecoderDoneEndsTheStream,ai/openai_stream_decoder_test.go::TestOpenAIStreamDecoderEOFAndDoneAreNotValues,ai/openai_stream_decoder_test.go::TestOpenAIStreamDecoderErrorEvent,ai/openai_stream_decoder_test.go::TestOpenAIStreamDecoderSSEAndLineSemantics,ai/openai_test.go::TestParseSSEUnknownFinishEmitsError,ai/sdk_sse_oracle_test.go::TestOpenAIStreamDecoderMatchesSDK,ai/sse_error_parity_test.go::TestProvidersHandleInStreamErrorsLikePinnedSDKs,ai/sse_parity_test.go::TestProviderSSEFramingAndEOFRules - Release note: The openai stream decoder follows openai 7.19.0, the version packages/ai declares: the stream ends at [DONE] without waiting for the connection to close, the last event is delivered when the server omits its blank line, [DONE] must be the whole data, and an event named error throws an APIError.
BF-24a9b69
fix(hljs): a resumed run counts the scan iterations before its checkpoint
- Source: PiG's own defect
- Symptom: fix(hljs): a resumed run counts the scan iterations before its checkpoint
- Root cause: A resumed run started counting at 0, so a whole run could throw (Pi prints the escaped code) where the resumed run did not (PiG printed highlighted code); maxCheckpointIterations only narrowed that window. A checkpoint now records the iterations before it, ResumePoint carries them, tui's resume state keeps them, and the resumed run continues the count, so both runs see the same counter at every match. maxCheckpointIterations goes: points past it are now exact.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
24a9b69c2e(2026-10-11) - Tests:
tui/highlight_resume_test.go::TestStreamedHighlightEqualsAWholeRun,tui/internal/hljs/resume_test.go::TestHighlightResumableEqualsAFreshRun,tui/internal/hljs/resume_test.go::TestHighlightResumableFollowsLaterText,tui/internal/hljs/resume_test.go::TestResumedRunCountsTheIterationsOfAWholeRun
BF-25aa7fd
A subprocess extension's markdown transform that was busy past the five-second renderer inactivity boundary…
- Source: PiG's own defect
- Symptom: A subprocess extension's markdown transform that was busy past the five-second renderer inactivity boundary no longer leaves the message untransformed until something else draws it again: the host asks again once the extension answers the abandoned transform, or after a backoff that starts at five seconds and doubles to one minute for an extension that keeps missing the boundary, and the message transforms again on its next frame.
- Root cause: A synchronous extension markdown transform the renderer inactivity boundary gave up on returned its input, and the message stayed untransformed until something else drew it again; Pi runs transformers in process, so a Pi user never sees that. The proxy now keeps the abandoned transform by message text and context, asks the extension again with the same backoff as render proxies (renderRetry: 5 s doubling to 60 s per abandonment in a row; the late answer to the first abandonment ends the backoff early), and marks the message changed through the transform context's Retry, so the next frame transforms it and repaints. A retry is the only request for its transform in flight; messages with the…
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
84e70fc701(2026-10-13),25aa7fd80b(2026-10-14),5119ce3155(2026-10-14),d7855fd99b(2026-10-14) - Tests:
coding/extension/host/subprocess/markdown_transform_retry_test.go::TestMarkdownTransformAsksTheRestartedExtensionForAnAbandonedTransform,coding/extension/host/subprocess/markdown_transform_retry_test.go::TestMarkdownTransformBacksOffFromATransformerThatAlwaysAnswersLate,coding/extension/host/subprocess/markdown_transform_retry_test.go::TestMarkdownTransformBacksOffFromAnExtensionThatNeverAnswers,coding/extension/host/subprocess/markdown_transform_retry_test.go::TestMarkdownTransformDropsARetryNoMessageTakes,coding/extension/host/subprocess/markdown_transform_retry_test.go::TestMarkdownTransformRendersAgainOnceTheExtensionAnswersAnAbandonedTransform,coding/extension/host/subprocess/markdown_transform_retry_test.go::TestMarkdownTransformRetriesEveryMessageWithTheSameText,coding/extension/host/subprocess/render_retry_backoff_test.go::TestRenderRetryBackoffDoublesUpToItsCap,coding/extension/host/subprocess/render_retry_test.go::TestRenderProxyBacksOffFromARendererThatAlwaysAnswersLateand 6 more in docs/bug-ledger.json - Release note: A subprocess extension's markdown transform that was busy past the five-second renderer inactivity boundary no longer leaves the message untransformed until something else draws it again: the host asks again once the extension answers the abandoned transform, or after a backoff that starts at five seconds and doubles to one minute for an extension that keeps missing the boundary, and the message transforms again on its next frame.
BF-25c854d
Session directories (the agent directory, sessions and each project's directory) are created owner-only…
- Source: A Pi defect that PiG fixed first (divergence D132)
- Symptom: Session directories (the agent directory,
sessionsand each project's directory) are created owner-only (0700) instead of world-readable (D132, pi-mono #10770). - Root cause: the sessions are private data, and the report calls the default a security problem.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10770
- Removed when Pi fixes it: Pi creates session directories with mode 0700.
- Fix commits:
25c854d4cd(2026-10-10) - Tests:
internal/codingagent/session_dir_mode_test.go::TestSessionDirectoriesAreCreatedOwnerOnly - Release note: Session directories (the agent directory,
sessionsand each project's directory) are created owner-only (0700) instead of world-readable (D132, pi-mono #10770).
BF-2699291
The serializeConversation parity row records that Rust session entries still read an unpaired surrogate as…
- Source: PiG's own defect
- Symptom: The serializeConversation parity row records that Rust session entries still read an unpaired surrogate as U+FFFD, and is partial until that is decided.
- Root cause: A 6,000-message differential against Pi's own serializeConversation (node + dist) now has 0 differences in Go and Python. Before the fix it had 53 in Go and 33 in Python. The conformance row cannot see this, because the harness decodes reports with encoding/json, which turns every lone surrogate into U+FFFD.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),058e7a0e4b(2026-10-11),2699291af5(2026-10-11),a178939d6a(2026-10-11),a4e5b071cb(2026-10-11),e8473d13c5(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/cli/extension_host_wiring_binary_test.go::TestHeadlessExtensionSessionActionsMatchPi,extensions/sdk-py/tests/test_compaction_estimate.py,extensions/sdk-py/tests/test_serialize_conversation_surrogates.py,extensions/sdk/compaction_estimate_test.go::TestEstimateTokensCountsUTF16UnitsAndJSONStringifyLengthand 8 more in docs/bug-ledger.json - Release note: The serializeConversation parity row records that Rust session entries still read an unpaired surrogate as U+FFFD, and is partial until that is decided.
BF-26b6705
The RPC test harness no longer races on its recorded output error (found by a go test -race ./coding/...…
- Source: PiG's own defect
- Symptom: The RPC test harness no longer races on its recorded output error (found by a
go test -race ./coding/...run); test-only. - Root cause: rpcProcess.outputErr is set by the output-reading goroutine on a decode error or at end of stream while tests read it to report a failure. TestRPCModeSessionReplacementAndExport read it after its last response, without waiting for the stream to close: go test -race ./coding/... reported the write (rpc_ui_integration_test.go:135) against that read (rpc_resume_integration_test.go:89). The error is behind a mutex now (setOutputErr, outputError) for the writer and the three readers.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
26b6705d36(2026-10-11) - Tests:
coding/cli/rpc_resume_integration_test.go::TestRPCModeSessionReplacementAndExport - Release note: The RPC test harness no longer races on its recorded output error (found by a
go test -race ./coding/...run); test-only.
BF-26d302a
A login flow's select prompt opens a selector in the login dialog, as Pi's does, instead of ending the…
- Source: PiG's own defect
- Symptom: A login flow's
selectprompt opens a selector in the login dialog, as Pi's does, instead of ending the login as "Login cancelled". - Root cause:
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
nativeExtensionProviders.get(id) ?? builtins.get(id)); registryAuthConfig now does the same, so the methods come from the retained provider object on every register, refresh and publication path; - the host carrier did not map auth.apiKey.login, auth.oauth.login or loginLabel. It now does, and their prompt and notify reverse calls decode into the closed AuthPrompt and AuthEvent unions (auth/types.ts); any other type fails the login with an…
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
26d302a2d7(2026-10-09, #201) - Tests:
coding/extension/host/subprocess/native_provider_login_test.go::TestDecodeNativeLoginCallback,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeAuthInteractionAnswersTheLoginDialog,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeProviderCarrierCarriesDeclaredLogins,coding/extension/host/subprocess/native_provider_oauth_test.go::TestNativeProviderOAuthLoginCallbacks,internal/codingagent/native_provider_login_test.go::TestNativeProviderAPIKeyLoginRunsDeclaredLogin,internal/codingagent/native_provider_login_test.go::TestNativeProviderAPIKeyLoginWithoutDeclaredLoginUsesGenericPrompt,internal/codingagent/native_provider_login_test.go::TestNativeProviderLoginOffersDeclaredMethods,internal/codingagent/native_provider_login_test.go::TestNativeProviderLoginRejectsUnknownInteractionTypesand 1 more in docs/bug-ledger.json - Release note: A login flow's
selectprompt opens a selector in the login dialog, as Pi's does, instead of ending the login as "Login cancelled". Cancelling the selector cancels the login and returns to the menu it was started from. This applies to provider objects and toconfig.oauthproviders.
BF-26d302a-2
A provider object's own auth.apiKey.login runs in the login dialog, as Pi's showApiKeyLoginDialog runs…
- Source: PiG's own defect
- Symptom: A provider object's own
auth.apiKey.loginruns in the login dialog, as Pi'sshowApiKeyLoginDialogruns it, so a provider that asks for its base URL before the key stores both. - Root cause:
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
nativeExtensionProviders.get(id) ?? builtins.get(id)); registryAuthConfig now does the same, so the methods come from the retained provider object on every register, refresh and publication path; - the host carrier did not map auth.apiKey.login, auth.oauth.login or loginLabel. It now does, and their prompt and notify reverse calls decode into the closed AuthPrompt and AuthEvent unions (auth/types.ts); any other type fails the login with an…
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
26d302a2d7(2026-10-09, #201) - Tests:
coding/extension/host/subprocess/native_provider_login_test.go::TestDecodeNativeLoginCallback,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeAuthInteractionAnswersTheLoginDialog,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeProviderCarrierCarriesDeclaredLogins,coding/extension/host/subprocess/native_provider_oauth_test.go::TestNativeProviderOAuthLoginCallbacks,internal/codingagent/native_provider_login_test.go::TestNativeProviderAPIKeyLoginRunsDeclaredLogin,internal/codingagent/native_provider_login_test.go::TestNativeProviderAPIKeyLoginWithoutDeclaredLoginUsesGenericPrompt,internal/codingagent/native_provider_login_test.go::TestNativeProviderLoginOffersDeclaredMethods,internal/codingagent/native_provider_login_test.go::TestNativeProviderLoginRejectsUnknownInteractionTypesand 1 more in docs/bug-ledger.json - Release note: A provider object's own
auth.apiKey.loginruns in the login dialog, as Pi'sshowApiKeyLoginDialogruns it, so a provider that asks for its base URL before the key stores both. Each answered prompt stays visible in the dialog. A provider object without its own API-key login keeps the generic API-key prompt.
BF-26d302a-3
A provider object's login that sends a prompt or event type Pi does not define fails with an error that names…
- Source: PiG's own defect
- Symptom: A provider object's login that sends a prompt or event type Pi does not define fails with an error that names the type, instead of being answered as a text prompt or shown as progress.
- Root cause:
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
nativeExtensionProviders.get(id) ?? builtins.get(id)); registryAuthConfig now does the same, so the methods come from the retained provider object on every register, refresh and publication path; - the host carrier did not map auth.apiKey.login, auth.oauth.login or loginLabel. It now does, and their prompt and notify reverse calls decode into the closed AuthPrompt and AuthEvent unions (auth/types.ts); any other type fails the login with an…
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
26d302a2d7(2026-10-09, #201) - Tests:
coding/extension/host/subprocess/native_provider_login_test.go::TestDecodeNativeLoginCallback,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeAuthInteractionAnswersTheLoginDialog,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeProviderCarrierCarriesDeclaredLogins,coding/extension/host/subprocess/native_provider_oauth_test.go::TestNativeProviderOAuthLoginCallbacks,internal/codingagent/native_provider_login_test.go::TestNativeProviderAPIKeyLoginRunsDeclaredLogin,internal/codingagent/native_provider_login_test.go::TestNativeProviderAPIKeyLoginWithoutDeclaredLoginUsesGenericPrompt,internal/codingagent/native_provider_login_test.go::TestNativeProviderLoginOffersDeclaredMethods,internal/codingagent/native_provider_login_test.go::TestNativeProviderLoginRejectsUnknownInteractionTypesand 1 more in docs/bug-ledger.json - Release note: A provider object's login that sends a prompt or event type Pi does not define fails with an error that names the type, instead of being answered as a text prompt or shown as progress.
BF-27a1650
The keybindings manager's key text and key matching now cover every keybinding the merged table defines…
- Source: PiG's own defect
- Symptom: A
tui.*id had no key text and matched nothing through the coding-agent manager, and an unknown id echoed its user keys. - Root cause: PiG's KeyText and Matches read km.resolved (app.* only): keyText("tui.select.confirm") was "" and matches("\r", "tui.select.confirm") false; getKeys echoed the user keys of an unknown id (TUI bridge). They now share keysOf over the merged table, which returns nothing for an undefined id.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e1abc6c615(2026-10-11),27a1650048(2026-10-12) - Tests:
internal/codingagent/keybindings_user_oracle_test.go::TestUserKeybindingsMatchPi - Release note: The keybindings manager's key text and key matching now cover every keybinding the merged table defines, including the
tui.*ones, and no keys for an id no definition names, as Pi'sKeybindingsManager.getKeys/keyText/matchesdo. Before, atui.*id had no key text and matched nothing through the coding-agent manager, and an unknown id echoed its user keys.
BF-27ddc30
A tool card shows the arguments of a tool call while they stream, as Pi does: a streamed write card shows its…
- Source: PiG's own defect
- Symptom: A tool card shows the arguments of a tool call while they stream, as Pi does: a streamed write card shows its first lines and the "(N more lines, M total" hint as the content arrives, instead of a bare
write ...header until the last delta. - Root cause: AppendOutput converted each chunk to bytes for StripANSI and back to a string, two copies per chunk of
!bash output that the executor had already stripped. StripANSIString returns clean text as it is, and the CR normalization copies only when a chunk has a carriage return. - User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
27ddc30867(2026-10-13),5d2eb77efe(2026-10-13),a98ed4d6ae(2026-10-13) - Tests:
internal/codingagent/interactive_events_stream_args_test.go::TestInteractiveStreamedToolArgsDeltaCostIsFlat,internal/codingagent/interactive_events_write_stream_oracle_test.go::TestInteractiveStreamedWriteCardMatchesPiOracle,internal/codingagent/tool_builtin_renderers_args_cost_test.go::TestEditRenderersDoNotReencodeUnchangedArgs,tui/bash_execution_append_cost_test.go::TestBashExecutionAppendOutputDoesNotCopyCleanChunks,tui/tool_execution_streamed_args_test.go::TestStreamedArgsEncodeOncePerDraw - Release note: A tool card shows the arguments of a tool call while they stream, as Pi does: a streamed write card shows its first lines and the "(N more lines, M total" hint as the content arrives, instead of a bare
write ...header until the last delta. The card reads the arguments the provider's streaming JSON parser has already decoded and encodes them only when it draws, so a delta costs the same however long the arguments are (10 allocations and 1.7 KB per delta at 1 KB and at 504 KB received).
BF-27f3324
The npm release packer reads npm pack --json output from npm 12, which keys the result by package name, as…
- Source: PiG's own defect
- Symptom: The npm release packer reads
npm pack --jsonoutput from npm 12, which keys the result by package name, as well as npm 11's array (Pi 6fb2e781). - Root cause: #10415: Pi main.ts:80-97 readPipedStdin waits for EOF; PiG ported it, so an open idle pipe hangs startup. --no-stdin skips the read; default unchanged (D-PIBUG-10415). pack_npm.py: npm 12 prints pack --json keyed by package name; port Pi 's shape handling.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
27f33240ee(2026-10-09) - Tests:
coding/cli/no_stdin_test.go::TestNoStdinStartsWithAnOpenInputPipe,coding/cli/no_stdin_test.go::TestParseArgsNoStdin - Release note: The npm release packer reads
npm pack --jsonoutput from npm 12, which keys the result by package name, as well as npm 11's array (Pi 6fb2e781).
BF-28b8425
Blank-key detection for compat API keys, the Vertex API key and the Vertex custom base URL trim whitespace…
- Source: PiG's own defect
- Symptom: Blank-key detection for compat API keys, the Vertex API key and the Vertex custom base URL trim whitespace the way JavaScript does: a value that is only a byte-order mark counts as blank, and a leading or trailing U+0085 is kept, as in Pi.
- Root cause: hasExplicitApiKey (compat.ts:220), resolveApiKey/isPlaceholderApiKey (google-vertex.ts:429-437) and resolveCustomBaseUrl (google-vertex.ts:407) are cut from Pi's sources (testdata/trim_sites.mjs) and run on 4500 values padded with every character that String.prototype.trim and strings.TrimSpace treat differently (U+FEFF, U+0085, U+180E, U+200B, U+2028/9, U+00A0, U+3000...). Go's TrimSpace strips U+0085 and keeps U+FEFF, JS the opposite, so a BOM-only key counted as present and a BOM-wrapped URL kept its BOM (red before). The three sites use trimJSWhitespace.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
093cf5c781(2026-10-09),0b4e1e3376(2026-10-09),28b8425efa(2026-10-09),90882933f6(2026-10-09) - Tests:
ai/azure_config_oracle_test.go::TestAzureBaseURLNormalizationMatchesPi,ai/codex_vertex_urls_oracle_test.go::TestCodexWebSocketAndVertexVersionURLsMatchPi,ai/error_body_oracle_test.go::TestErrorBodyHelpersMatchPiOnSeededProbes,ai/trim_sites_oracle_test.go::TestJSTrimSitesMatchPi,internal/evals/plan_oracle_test.go::TestPlanMatchesPiOnSeededProbes - Release note: Blank-key detection for compat API keys, the Vertex API key and the Vertex custom base URL trim whitespace the way JavaScript does: a value that is only a byte-order mark counts as blank, and a leading or trailing U+0085 is kept, as in Pi.
BF-28f8b3b
fix(tui): editor wrap of a one-grapheme segment wider than the width no longer overflows the stack (D158)
- Source: PiG's own defect
- Symptom: fix(tui): editor wrap of a one-grapheme segment wider than the width no longer overflows the stack (D158)
- Root cause: Pi's wordWrapLine re-wraps a segment wider than maxWidth with the same input when it is a single grapheme (a tab at width <= 2, a double-width character at width 1) and throws RangeError; the Go stack overflow is fatal and kills the process. The grapheme now stays one chunk. Found by the new FuzzEditorInputKeepsRenderValid (seed kept); red: TestEditorWrapsOverWideGraphemeWithoutRecursion dies with 'fatal error: stack overflow' without the fix.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
28f8b3bf2e(2026-10-10) - Tests:
ai/sse_fuzz_test.go::FuzzSSEDecoderIsIndependentOfChunking,tui/editor_fuzz_test.go::FuzzEditorInputKeepsRenderValid,tui/editor_wide_grapheme_test.go::TestEditorWrapsOverWideGraphemeWithoutRecursion,tui/markdown_fuzz_test.go::FuzzMarkdownRenderStaysWithinWidth
BF-2a055a7
An extension dialog (select, confirm, input, editor) opened while a focused ctx.ui.custom component…
- Source: PiG's own defect
- Symptom: A subprocess extension's dialog waited until the custom component closed.
- Root cause: Ruling 2. With a focused custom overlay, tui.ts:598-665 records the dialog as blocking the overlay's focus restore and, once the hidden dialog is unmounted, resumes the overlay, so the custom overlay has focus and the next key again. An editor-slot custom component is cleared out of the slot by the dialog, so focus goes to the editor. The custom component receives no input while the dialog is up. Two custom components never wait for each other in Pi (showExtensionCustom has no cross-call mutex); that is unchanged here (subprocess custom components still serialize, the known gap in docs/findings/fix-overlay-shim.md).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),1848633092(2026-10-13),2a055a7a3a(2026-10-13) - Tests:
coding/extension/host/subprocess/host_calls_test.go::TestReadLoopWidgetPushOrdersAgainstQueuedSlotCalls,coding/extension/host/subprocess/ui_bridge_test.go::TestInteractiveDialogOpensOverFocusedOverlay,coding/extension/host/subprocess/ui_prompt_test.go::TestUIBridgePromptScopeDialogOverCustomAndNoUI,coding/extension/host/subprocess/view_terminal_parity_test.go::TestViewsLeaveTheTerminalBytesUnchanged,coding/extension/host/subprocess/widget_key_namespace_test.go::TestTwoExtensionsSettingOneWidgetKeyShareIt,internal/codingagent/ext_ui_dialog_focus_test.go::TestExtensionDialogTakesFocusFromFocusedCustomOverlay,internal/codingagent/ext_ui_dialog_focus_test.go::TestExtensionDialogTakesTerminalFromFocusedRemoteOverlay,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResultand 7 more in docs/bug-ledger.json - Release note: An extension dialog (
select,confirm,input,editor) opened while a focusedctx.ui.customcomponent holds the terminal now shows at once and takes focus, as in Pi. Closing the dialog gives focus back to a custom overlay, which receives the next key. A custom component in the editor slot that the dialog replaced stays off screen, and focus goes to the editor. Before, a subprocess extension's dialog waited until the custom component closed. A dialog from an in-process extension opened at once, but the custom component kept focus, and a subprocess custom component kept reading the keys meant for the dialog.
BF-2a0fb96
/cost lists its tools in a stable order: formatCostSummary ranged over snap.ToolTotals, so the Tools:…
- Source: PiG's own defect
- Symptom:
/costlists its tools in a stable order:formatCostSummaryranged oversnap.ToolTotals, so theTools:lines came out in a different order on every call (Go map iteration). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2a0fb968de(2026-10-11) - Tests:
internal/codingagent/slash_cost_order_test.go::TestFormatCostSummaryListsToolsInStableNameOrder - Release note:
/costlists its tools in a stable order:formatCostSummaryranged oversnap.ToolTotals, so theTools:lines came out in a different order on every call (Go map iteration). They are now sorted by name. RegressionTestFormatCostSummaryListsToolsInStableNameOrder(red before: fails on the first run with 24 tools)./costhas no Pi counterpart, so the name order is PiG's choice. Found by scanning production code for map ranges that feed output; the same pattern (a map range whose first failing entry names the error) remains incoding/packagecontent(validateMCPDocument,validateComposeClosure) andcoding/hookconfig.Parse, which belong to other lanes.
BF-2a919a4
An indented continuation line of a list item loses up to four spaces, a lazy line after a quoted list item…
- Source: PiG's own defect
- Symptom: An indented continuation line of a list item loses up to four spaces, a lazy line after a quoted list item that ends in spaces is a paragraph of its own, the last list item's text is trimmed, and a text of only link definitions or an empty quote followed by blank lines renders its empty row.
- Root cause: TestParseMatchesTheLibraryOnMutatedCorpus and TestLexerMatchesTheLibraryOnMutatedCorpus compare eemeli/yaml 2.9.0 with Parse/lex on 6000 mutated corpus texts (40 differed before, 15-17 per 20000 after the first fixes). Fixed: an unterminated quote (buffer.substring(0, -1) is empty: the error is at the end of input, not after the first line); a line starting with a non-ASCII character reduced the continuation indent because the lexer looked at the second byte, not the second UTF-16 unit (charAt(1)); Invalid escape sequence raw text and the 1024-unit implicit key limit counted bytes; anchors reject JavaScript whitespace (RE2 \s is narrower); -0 is 0 as an omap/set key; Buffer.from(.., base64)…
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2a919a4c3c(2026-10-10),4e5b684ecd(2026-10-10),ac0dc3bee4(2026-10-10),b5b7b43f82(2026-10-10),cdf0be76e4(2026-10-10) - Tests:
internal/yaml12/parse_mutation_test.go::TestLexerMatchesTheLibraryOnMutatedCorpus,internal/yaml12/parse_mutation_test.go::TestParseMatchesTheLibraryOnMutatedCorpus,internal/yaml12/parse_mutation_test.go::TestParseMatchesTheLibraryOnUTF16Offsets,internal/yaml12/parse_test.go::TestParseMatchesTheLibrary,tui/markdown_render_oracle_test.go::TestMarkdownRenderMatchesPi - Release note: An indented continuation line of a list item loses up to four spaces, a lazy line after a quoted list item that ends in spaces is a paragraph of its own, the last list item's text is trimmed, and a text of only link definitions or an empty quote followed by blank lines renders its empty row.
BF-2ab79ce
An extension provider whose streamSimple hands its request to pi-ai's compat streamSimple (or stream)…
- Source: PiG's own defect
- Symptom: An extension provider whose
streamSimplehands its request to pi-ai's compatstreamSimple(orstream) for a stock API, aspi-commandcode-providerdoes, no longer re-enters its ownstreamSimplewithout bound. - Root cause: pi-commandcode-provider registers a provider whose streamSimple forwards to pi-ai's streamSimple for the model's real wire. Pi dispatches that call on model.api (compat.ts:252-293). PiG looked the provider up by name, found the extension's own streamSimple and called it again, without end, until the packed Node process ran out of memory, missed its heartbeat and was killed, taking the healthy members that shared it down too.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2ab79ce358(2026-10-06, #166) - Tests:
coding/custom_provider_stream_upstream_test.go::TestRegisteredProviderCallbackServesOnlyItsAPI,coding/delegating_provider_subprocess_test.go::TestRegisteredProviderDelegatingToCompatStreamSimpleReachesTheAPI,coding/model_api_request_test.go::TestIndependentAPIRequestSkipsRegisteredProviderCallback - Release note: An extension provider whose
streamSimplehands its request to pi-ai's compatstreamSimple(orstream) for a stock API, aspi-commandcode-providerdoes, no longer re-enters its ownstreamSimplewithout bound. The request now reaches the stock API's endpoint, as under Pi, instead of growing the Go and Node processes until the extension stopped answering heartbeats and the packed cell was killed (#164, reported by @xinaps-dev).
BF-2ab79ce-2
A provider registered with its own streamSimple runs that callback only for its models whose api is the…
- Source: PiG's own defect
- Symptom: A provider registered with its own
streamSimpleruns that callback only for its models whoseapiis the provider's registeredapi, as under Pi. - Root cause: pi-commandcode-provider registers a provider whose streamSimple forwards to pi-ai's streamSimple for the model's real wire. Pi dispatches that call on model.api (compat.ts:252-293). PiG looked the provider up by name, found the extension's own streamSimple and called it again, without end, until the packed Node process ran out of memory, missed its heartbeat and was killed, taking the healthy members that shared it down too.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2ab79ce358(2026-10-06, #166) - Tests:
coding/custom_provider_stream_upstream_test.go::TestRegisteredProviderCallbackServesOnlyItsAPI,coding/delegating_provider_subprocess_test.go::TestRegisteredProviderDelegatingToCompatStreamSimpleReachesTheAPI,coding/model_api_request_test.go::TestIndependentAPIRequestSkipsRegisteredProviderCallback - Release note: A provider registered with its own
streamSimpleruns that callback only for its models whoseapiis the provider's registeredapi, as under Pi. A model of that provider that names another API, such asopenai-completions, now reaches that API's implementation instead of the callback. This applies to extension registrations and toModelRuntime.RegisterProvider.
BF-2ad7249
fix(ledger): close the getJsonSchemaToolParameters and google convertTools rows
- Source: PiG's own defect
- Symptom: fix(ledger): close the getJsonSchemaToolParameters and google convertTools rows
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
2ad72495fe(2026-10-10) - Tests:
ai/google_convert_tools_upstream_test.go::TestGoogleSharedConvertToolsUpstream
BF-2be0883
fix(extensions): parallel Node runtime linking reports the walk-order first failure under every schedule
- Source: PiG's own defect
- Symptom: fix(extensions): parallel Node runtime linking reports the walk-order first failure under every schedule
- Root cause: When i would also have failed, the reported error was the later one, not the walk-order first one the commit claims. The new TestLinkInWalkOrderRunsEveryIndexBelowTheFirstFailure fails under that rule in 2 of 3 -race runs and passes 5/5 -race runs with this fix (TestNodeRuntimeLinkReportsTheWalkOrderFirstFailure stays green under both). The workers now record the lowest failing index (a CAS minimum) and skip only indices above it, so every index below the lowest failure runs and its error is the first in errs. The loop moves into linkInWalkOrder so the schedule is testable without a filesystem; TestLinkInWalkOrderStopsOnACancelledContext pins the context check.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
2be08834a3(2026-10-11) - Tests:
coding/extension/host/subprocess/node_runtime_cache_test.go::TestLinkInWalkOrderRunsEveryIndexBelowTheFirstFailure,coding/extension/host/subprocess/node_runtime_cache_test.go::TestLinkInWalkOrderStopsOnACancelledContext,coding/extension/host/subprocess/node_runtime_cache_test.go::TestNodeRuntimeLinkReportsTheWalkOrderFirstFailure
BF-2c1959c
A Mistral stream is no longer aborted mid-generation by the request timeout: the timeout now covers only the…
- Source: PiG's own defect
- Symptom: A Mistral stream is no longer aborted mid-generation by the request timeout: the timeout now covers only the wait for response headers (D127, pi-mono #10609, ported from Pi commit 6b07b4e5).
- Root cause: Port of pi-mono: a long Mistral stream was aborted when timeoutMs elapsed. The timeout now ends when headers arrive.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2c1959cb36(2026-10-09) - Tests:
ai/mistral_header_timeout_test.go::TestMistralTimeoutCoversOnlyResponseHeaders,ai/mistral_regression_test.go::TestMistralRequestLifetimeClosesBodyOnce - Release note: A Mistral stream is no longer aborted mid-generation by the request timeout: the timeout now covers only the wait for response headers (D127, pi-mono #10609, ported from Pi commit 6b07b4e5).
BF-2c5db8b
A Piglet Binary with a fused Go extension starts within a few milliseconds of Stock PiG
- Source: PiG's own defect
- Symptom: A Piglet Binary with a fused Go extension starts within a few milliseconds of Stock PiG.
- Root cause: A Piglet Binary with one fused Go extension took 72.1 ms for -p with the faux provider against 31.7 ms for Stock PiG. The extension host encoded the whole model catalog (more than 1 MB of JSON) three times while it wired model operations and sent it to every extension, though the Go, Rust and Python SDKs answer ctx.modelRegistry with a blocking getModelRegistryState call and discard the snapshot.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2c5db8b20a(2026-10-06) - Tests: None recorded.
- Release note: A Piglet Binary with a fused Go extension starts within a few milliseconds of Stock PiG. On an Apple M4 Pro (median of 20, faux provider),
-ptook 72.1 ms against 31.7 ms for Stock PiG and now takes 33.0 ms, and the first typed key showed in the editor after 71.0 ms against 33.6 ms and now after 33.4 ms. The extension host encoded the whole model catalog (more than 1 MB of JSON) three times at startup for every extension. Now it builds and sends that snapshot only to Node extensions, which answerctx.modelRegistryreads from it. Go, Rust and Python extensions read the registry from the host when they ask, as before, so Stock PiG with such extensions starts faster too. See D19 indocs/additive-features.md.
BF-2c88c5f
Ill-formed UTF-8 in a string that PiG re-encodes as JSON (an extension's raw JSON, tool schemas, code-mode…
- Source: PiG's own defect
- Symptom: Ill-formed UTF-8 in a string that PiG re-encodes as JSON (an extension's raw JSON, tool schemas, code-mode keys, Bedrock and Google request members) is written as
JSON.stringifywrites the decoded text: one literal U+FFFD for each ill-formed sequence, instead of one\ufffdescape per byte. - Root cause: Design step 1 ("internal/jsnumber.String, jsstring and jsonstringify's string writer become callers"): - internal/jsstring.AppendString is jsparity.AppendString. It was an ASCII fast path plus MarshalJSON through the SDK encoder. - internal/jsnumber.String writes finite numbers with jsparity.AppendNumber instead of a safe-integer branch plus json.Marshal. - internal/jsonstringify's canonicalize writes numbers with jsparity.AppendNumber. stringToken is JSON.stringify(JSON.parse(token)): jv.Parse decodes to WTF-8 and jsparity.AppendString writes it, replacing a hand-written escape rewriter.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2c88c5fd2e(2026-10-10),80c797da93(2026-10-10),afa1dd5185(2026-10-10),f02269c6e1(2026-10-10),fe625bb5dd(2026-10-10) - Tests:
ai/anthropic_replaced_payload_order_oracle_test.go::TestAnthropicReplacedPayloadKeepsPiMemberOrder,ai/bedrock_request_body_oracle_test.go::TestBedrockRequestBodyMatchesPi,ai/bedrock_wire_metadata_order_test.go::TestBedrockStringMapEnumeratesLikeAJavaScriptObject,ai/google_request_body_oracle_test.go::TestGoogleRequestBodyMatchesPi,ai/mistral_request_body_oracle_test.go::TestMistralRequestBodyMatchesPi,ai/mistral_request_body_oracle_test.go::TestMistralTypedWirePayloadMatchesTheGeneralLowering,ai/openai_codex_websocket_frame_oracle_test.go::TestCodexWebSocketFramesMatchPi,ai/provider_ecmascript_whitespace_test.go::TestMistralAssistantBlankUsesECMAScriptTrimand 17 more in docs/bug-ledger.json - Release note: Ill-formed UTF-8 in a string that PiG re-encodes as JSON (an extension's raw JSON, tool schemas, code-mode keys, Bedrock and Google request members) is written as
JSON.stringifywrites the decoded text: one literal U+FFFD for each ill-formed sequence, instead of one\ufffdescape per byte.
BF-2d05dc8
A models.json cost that sets tiers: [] (a custom model's cost, or a modelOverrides cost) keeps…
- Source: PiG's own defect
- Symptom: A models.json cost that sets
tiers: [](a custom model's cost, or amodelOverridescost) keepscost.tiers: []in an extension'sctx.modeland model lists and in RPCget_stateandget_available_models, as Pi keeps the authored cost, instead of dropping the member. - Root cause: Pi's ModelCost.tiers is optional: provider-composer.ts:233 keeps a models.json model's authored cost and :193 an override's
override.cost.tiers ?? model.cost.tiers, so ctx.model, the model registry and RPC get_state/get_available_models show an authored [] and no member when none was written. ModelCost.Tiers dropped [] (omitempty), and the copies on the composer path (append to nil) turned [] into nil. - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2d05dc8572(2026-10-13),3789a93b5c(2026-10-13) - Tests:
ai/model_cost_tiers_presence_test.go::TestBuiltinFallbackCostsWithoutTiersHaveNone,ai/model_cost_tiers_presence_test.go::TestComposedModelRecordKeepsTiersPresence,ai/model_cost_tiers_presence_test.go::TestModelCostKeepsTiersPresence,cmd/gen-models/ai_gateway_models_test.go::TestAiGatewayListingWindowsAreWholeTokenCounts,cmd/gen-models/main_test.go::TestAllowedFallbackModelsLiteralKeepsTiersPresence,coding/cli/rpc_model_cost_tiers_test.go::TestRPCModelValueKeepsCostTiersPresence,coding/model_object_cost_tiers_test.go::TestModelObjectKeepsAnAuthoredEmptyCostTiers - Release note: A models.json cost that sets
tiers: [](a custom model's cost, or amodelOverridescost) keepscost.tiers: []in an extension'sctx.modeland model lists and in RPCget_stateandget_available_models, as Pi keeps the authored cost, instead of dropping the member. A cost withouttiersstill has none.
BF-2d71556
fix(codingagent): queued-message lines use the dim foreground; hint separator closes only the foreground
- Source: PiG's own defect
- Symptom: fix(codingagent): queued-message lines use the dim foreground; hint separator closes only the foreground
- Root cause: Pi interactive-mode.ts:4729-4737 renders queued steering/follow-up lines and the dequeue hint as theme.fg("dim", ...) and :1056 joins the compact startup hints with theme.fg("muted", " · "). Pig used the muted colour (not dim) and a full reset (ESC[0m) for the queued lines, and a full reset after the separator. Tests TestPendingDisplayLinesUseTheDimForeground and TestStartupKeybindHintsSeparatorIsTheMutedForeground red before the fix.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
2d71556f55(2026-10-10) - Tests:
internal/codingagent/pending_display_test.go::TestPendingDisplayLinesUseTheDimForeground,internal/codingagent/startup_hints_test.go::TestStartupKeybindHintsSeparatorIsTheMutedForeground
BF-2d99958
A terminal's reply to the startup color query now applies the colors and ends the startup wait before the…
- Source: PiG's own defect
- Symptom: A terminal's reply to the startup color query now applies the colors and ends the startup wait before the next input is read, as in Pi.
- Root cause: Pi settles the color query inside the input handler, and its .then(apply) and the await continuation run as microtasks before the next input chunk. The owner loop now receives the query's completion itself: the input that completes the query applies its colors before it returns, a task posted at the timeout applies a timed-out query, and the startup color wait returns before it reads more input once the latest query applied.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2d99958e18(2026-10-13),f12896bcc6(2026-10-13) - Tests:
internal/codingagent/interactive_init_state_pty_test.go::TestInitBuildsTheInteractiveStateBeforeTheSessionBindChild,internal/codingagent/startup_color_wait_render_test.go::TestRenderRequestedWhileStartupWaitsForTheTerminalColorsPaints,internal/codingagent/startup_query_order_pty_test.go::TestStartupWritesTheTerminalColorQueryBeforeTheFirstFrame,internal/codingagent/startup_query_order_pty_test.go::TestStartupWritesTheTerminalColorQueryBeforeTheFirstFrameChild,internal/codingagent/startup_submit_test.go::TestColorReplyCompletesTheStartupWaitBeforeTheNextInput,internal/codingagent/startup_submit_test.go::TestPromptAfterTheColorReplyReachesTheSubmitHandler - Release note: A terminal's reply to the startup color query now applies the colors and ends the startup wait before the next input is read, as in Pi. A prompt typed right after the terminal answered, for example by a client that types as soon as the editor shows, reaches the ordinary submit handler instead of reporting "Startup is still in progress".
BF-2ea8ad2
The openai-completions, openai-responses and openrouter-images requests build their URL as the openai SDK…
- Source: PiG's own defect
- Symptom: The openai-completions, openai-responses and openrouter-images requests build their URL as the openai SDK does (base query rewritten through qs, one slash dropped, WHATWG normalisation) and a URL with credentials is refused as fetch refuses it ("Connection error."), where Pig sent the credentials as Basic auth.
- Root cause: Not recorded.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2ea8ad24ae(2026-10-10),a8ff0c7afe(2026-10-10) - Tests:
ai/sdk_request_url_oracle_test.go::TestSDKProvidersRefuseCredentialsLikeFetch,ai/sdk_request_url_oracle_test.go::TestSDKProvidersRequestURLMatchesSDK - Release note: The openai-completions, openai-responses and openrouter-images requests build their URL as the openai SDK does (base query rewritten through qs, one slash dropped, WHATWG normalisation) and a URL with credentials is refused as fetch refuses it ("Connection error."), where Pig sent the credentials as Basic auth.
BF-2f881ba
"Your first extension" now says that pig install --validate-only exits 1 when the extension does not build
- Source: PiG's own defect
- Symptom: It said 0.
- Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
2f881baf82(2026-10-11) - Tests: None recorded.
- Release note: "Your first extension" now says that
pig install --validate-onlyexits 1 when the extension does not build. Before, it said 0.
BF-2fc2307
A scalar that names a collection tag (!!map 1, !!seq true) inside a complex mapping key stays plain in…
- Source: PiG's own defect
- Symptom: A scalar that names a collection tag (
!!map 1,!!seq true) inside a complex mapping key stays plain in the key's text, as the yaml library prints it; it used to be quoted (!!map "1") as a string scalar is, so the object key differed. - Root cause: getTagObject picks the tag by name, and the collection tags have no stringify, so the scalar reaches stringifyString with the caller's context: a text that would read as a number stays plain in a stringified complex key ('? [!!map 1]' keys the object '[ !!map 1 ]', the library; Pig quoted it). Found by the yaml12 mutated-corpus seed sweep (class 3 of the earlier note). The blank-line indentation inside stringified flow-map keys (class 2) remains.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2fc2307be4(2026-10-11) - Tests:
internal/yaml12/parse_mutation_test.go::TestParseMatchesTheLibraryOnCollectionTagsOnScalars - Release note: A scalar that names a collection tag (
!!map 1,!!seq true) inside a complex mapping key stays plain in the key's text, as the yaml library prints it; it used to be quoted (!!map "1") as a string scalar is, so the object key differed.
BF-305bd8b
A durable task that panics with a value whose String method panics now faults with the message `[object…
- Source: PiG's own defect
- Symptom: A durable task that panics with a value whose
Stringmethod panics now faults with the message[object Object], as Pi'serrorMessagegives for a thrown value that cannot be formatted, instead of Go's%!v(PANIC=...)text. - Root cause: 92 Pi cases (seven files, all new in 1.2.0). 85 ported, 7 skipped. Every Go test cites '<file>.test.ts:<line> it(...)'.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
305bd8be1f(2026-10-13) - Tests:
durable/harness/harness_listener_errors_test.go::TestAThrownValueThatCannotBeFormatted,durable/harness/tool_nested_test.go::TestAReplaySafeCallerReattachesToTheNestedCallsItMadeBeforeARestart,durable/harness/tool_nested_test.go::TestTheLeftRunningNestedCallsOfACallerInterruptedWhileItCleanedThemUpAreAborted,durable/harness/tool_nested_test.go::TestToolTaskMigratesVersion1InputToAModelIssuedCall - Release note: A durable task that panics with a value whose
Stringmethod panics now faults with the message[object Object], as Pi'serrorMessagegives for a thrown value that cannot be formatted, instead of Go's%!v(PANIC=...)text.
BF-30d4a6d
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
30d4a6d805(2026-10-13) - Tests: None recorded.
BF-30e47f4
fix(tui): SetThemeInstance notifies the active theme observers, as Pi's setThemeInstance runs its callback
- Source: PiG's own defect
- Symptom: fix(tui): SetThemeInstance notifies the active theme observers, as Pi's setThemeInstance runs its callback
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
30e47f4408(2026-10-21) - Tests: None recorded.
BF-3154dfb
Starting a Go extension no longer fails with `hash <dir> after its build: open …/release/deps/rmeta…: no…
- Source: PiG's own defect
- Symptom: Starting a Go extension no longer fails with
hash <dir> after its build: open …/release/deps/rmeta…: no such file or directorywhen a Cargo build runs in a directory itsgo.modreplaces. - Root cause: walkDirSources excluded build output only by directory name (target, dist, hidden, vendor, node_modules), so a target directory named anything else (rust-target) was walked as source, and any error from reading a directory another process had just deleted was fatal. runtimecell's hashGoModule already treats a tree marked with CACHEDIR.TAG as build output and a vanished directory as holding no source; the subprocess builder's walk never got the same rule.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
633152df76(2026-10-23) - Tests:
coding/extension/host/subprocess/hash_build_output_test.go::TestHashSourceDirFailsClosedOnUnreadableSourceFile,coding/extension/host/subprocess/hash_build_output_test.go::TestHashSourceDirIgnoresConcurrentChurnInReplacedModule,coding/extension/host/subprocess/hash_build_output_test.go::TestHashSourceDirSkipsCacheDirectoryTreesOfReplacedModules,test/extension-conformance/provider_stream_test.go::TestProviderProducersAcrossSDKs - Release note: Starting a Go extension no longer fails with
hash <dir> after its build: open …/release/deps/rmeta…: no such file or directorywhen a Cargo build runs in a directory itsgo.modreplaces. The source hash that guards a build against an edit made while it ran walked every replaced module in full, including a Cargo target directory not namedtarget, and a temp directory rustc deleted between the listing and the read failed the whole hash. The walk now skips a tree marked with aCACHEDIR.TAGsignature, as Cargo marks every target directory it creates, and a directory that vanishes mid-walk, as the packed-cell hash already did. A source file that cannot be read still fails the hash.
BF-31740ec
A waitForIdle() waiter registered during a run stays pending through agent_settled handlers and the…
- Source: A Pi defect that PiG fixed first (divergence D142)
- Symptom: A
waitForIdle()waiter registered during a run stays pending throughagent_settledhandlers and the prompts they defer, and an abort while settlement is in progress discards those prompts (D142, Pi #10704, #10705). - Root cause: A host that waits for idle must not report completion while work is still queued, and a stopped Session must not start provider requests.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10704
- Removed when Pi fixes it: Pi keeps registered waiters pending through settlement and discards deferred prompts on abort.
- Fix commits:
31740ec1fc(2026-10-10),35e46b114e(2026-10-10) - Tests:
coding/session_settled_idle_test.go::TestAbortDuringADeferredContinuationDiscardsTheRest,coding/session_settled_idle_test.go::TestAbortFromAgentSettledSubscriberDiscardsQueuedContinuation,coding/session_settled_idle_test.go::TestCustomMessageRunSettledHandlerAwaitsCompaction,coding/session_settled_idle_test.go::TestSettledHandlerCompactionKeepsTheContinuationQueuedBeforeIt,coding/session_settled_idle_test.go::TestWaitForIdleStaysPendingThroughEveryDeferredPrompt,coding/session_settled_idle_test.go::TestWaitForIdleStaysPendingThroughSettledHandlerCompactionAndContinuation - Release note: A
waitForIdle()waiter registered during a run stays pending throughagent_settledhandlers and the prompts they defer, and an abort while settlement is in progress discards those prompts (D142, Pi #10704, #10705). Anagent_settledhandler that awaits a compaction no longer deadlocks on the Session lock.
BF-31a7d77
SessionBeforeTreeResultSummary.Usage is typed as ai.Usage
- Source: PiG's own defect
- Symptom:
SessionBeforeTreeResultSummary.Usageis typed asai.Usage. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
31a7d77703(2026-10-06) - Tests:
coding/session_tree_test.go::TestNavigateTreeExtensionSummary - Release note:
SessionBeforeTreeResultSummary.Usageis typed asai.Usage.
BF-31cde9f
A Node extension that calls pi.appendEntry now sees the new entry in ctx.sessionManager.getEntries() in…
- Source: PiG's own defect
- Symptom: A Node extension that calls
pi.appendEntrynow sees the new entry inctx.sessionManager.getEntries()in the same handler, as in Pi. - Root cause: Pi's pi.appendEntry calls sessionManager.appendCustomEntry on the in-process log and then emits entry_appended (agent-session.ts:3406-3411), so ctx.sessionManager.getEntries() in the same handler holds the entry. The Node runtime sent the append to the host without waiting and read only the replicated log, so the robust-notes /robust-note command counted 0 notes where Pi counts 1.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
382156cdb7(2026-10-07),6f9132537e(2026-10-07),8d990c5d54(2026-10-07),a79de03e7f(2026-10-07),31cde9f009(2026-10-09),7489735c3f(2026-10-09) - Tests:
coding/cli/config_command_test.go::TestRunConfigCommandLocalRequiresTrustedProject,coding/cli/rpc_append_entry_visible_test.go::TestRPCNodeAppendEntryIsVisibleToTheSameHandlerAndEmitsEntryAppended,coding/extension/host/inproc/upstream_scoped_models_test.go::TestContextThinkingLevelReadsTheBoundRuntimeLive,coding/extension/host/subprocess/builder_node_entry_test.go::TestResolveNodeEntrypointImportsAnIndexMjsDirectoryAsJiti,internal/codingagent/interactive_test.go::TestHasAssistantUsageAfterCompaction,internal/packagemanager/package_manager_test.go::TestPackageManagerPersistsInstallAndRemoval,internal/packagemanager/package_manager_test.go::TestPackageManagerReportsInstalledPathsPerScope,internal/packagemanager/package_manager_test.go::TestPackageManagerReportsProgressToTheInstalledCallbackand 4 more in docs/bug-ledger.json - Release note: A Node extension that calls
pi.appendEntrynow sees the new entry inctx.sessionManager.getEntries()in the same handler, as in Pi. RPC mode and interactive mode still report the append (entry_appended).
BF-31d09fa
Fixed OpenRouter router models such as openrouter/auto showing a cost of -$1,000,000 per 1M tokens…
- Source: PiG's own defect
- Symptom: Fixed OpenRouter router models such as
openrouter/autoshowing a cost of -$1,000,000 per 1M tokens; OpenRouter's negative placeholder prices are now 0. - Root cause: OpenRouter's catalog is the user's own list: each network refresh passes the bundled models plus a newer pi.dev catalog through ai.PersonalizeOpenRouterCatalog and saves the result, which replaces the catalog (also offline); an empty saved list falls back to the bundled models, a failed personalization saves nothing. The registry refreshes through the provider's configured base URL (models.json or an extension's), as Models passes baseUrl in RefreshModelsContext. newCatalogRequest, readRemoteCatalog and isNewerCatalog factor out the shared parts of withRemoteCatalog. Pi's two personalized-catalog tests are ported; registry and provider mutants (nine) killed. docs/site/docs/providers.md…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
31d09fa092(2026-10-12),40dd5646a1(2026-10-12),5db6926738(2026-10-12) - Tests:
ai/models_runtime_refresh_upstream_test.go::TestModelsRuntimeRefreshUpstream,ai/openrouter_api_test.go::TestOpenRouterCostConvertsPricesAndFallsBackOnMissingInvalidAndNegativeRates,ai/openrouter_user_models_test.go::TestPersonalizeOpenRouterCatalogKeepsOnlyTheKeysChatModelsWithTheKeysLimitsAndPrices,ai/openrouter_user_models_test.go::TestPersonalizeOpenRouterCatalogRejectsOtherFailuresAndSkipsTheRequestWithoutAKey,ai/openrouter_user_models_test.go::TestPersonalizeOpenRouterCatalogReturnsTheCatalogUnchangedWhenTheEndpointRejectsTheKeyOrIsMissing,cmd/gen-models/openrouter_catalog_test.go::TestOpenRouterCostSerializesNegativeZeroAsZero,internal/codingagent/remote_catalog_personalized_test.go::TestPersonalizedCatalogProviderKeepsTheSavedListAndSavesNothingWhenPersonalizationFails,internal/codingagent/remote_catalog_personalized_test.go::TestPersonalizedCatalogProviderPersonalizesTheBuiltInModelsPlusANewerPiDevCatalogOnEveryRefreshand 4 more in docs/bug-ledger.json - Release note: Fixed OpenRouter router models such as
openrouter/autoshowing a cost of -$1,000,000 per 1M tokens; OpenRouter's negative placeholder prices are now 0.
BF-31d85ab
fix duplicate jsstring import in tui/text.go
- Source: PiG's own defect
- Symptom: fix duplicate jsstring import in tui/text.go
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
31d85abb57(2026-10-11) - Tests: None recorded.
BF-31f4517
Radius gateway and OAuth authorization URLs are parsed and serialized with the WHATWG URL parser, as Node's…
- Source: PiG's own defect
- Symptom: Go's
net/urlaccepted or reshaped many of these. - Root cause: 411 of the first 5000 differed after a first pass and 3937 of 5000 with net/url (red before): hosts not lowercased or IDN/IPv4/IPv6-normalized, userinfo not percent-encoded, schemeless and bad-port inputs accepted, default ports kept, an empty search dropping the '?' that Pi keeps. resolveGatewayPath and the new radiusAuthorizeURL (extracted from loginWithBrowser) now use nodeurl.ParseURL with two new methods, URL.ResolveAbsolutePath (opaque-path bases throw) and URL.WithSearch (the setter: '' removes the query, one leading '?' is dropped, the query percent-encode set with the special-scheme ' extra); the unused parseGatewayURL is gone.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
093cf5c781(2026-10-09),15a5c0b670(2026-10-09),2c04c1445a(2026-10-09),31f4517fbe(2026-10-09),7c4d874438(2026-10-09),d1b3ad0151(2026-10-09),d80c1cd150(2026-10-09) - Tests:
ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,ai/error_body_oracle_test.go::TestErrorBodyHelpersMatchPiOnSeededProbes,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,ai/radius_config_oracle_test.go::TestRadiusConfigMatchesPiOnSeededProbes,ai/radius_urls_oracle_test.go::TestRadiusURLsMatchPiOnSeededProbes,ai/trusted_url_oracle_test.go::TestTrustedVerificationURLsMatchPi,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,internal/nodeurl/url_oracle_test.go::TestParseMatchesNodeURL - Release note: Radius gateway and OAuth authorization URLs are parsed and serialized with the WHATWG URL parser, as Node's
new URLdoes. Hosts are lowercased and converted from IDN, IPv4 and IPv6 forms, userinfo is percent-encoded, a URL without a scheme or with a bad port is rejected, and an emptysearchkeeps a?. Before, Go'snet/urlaccepted or reshaped many of these.
BF-3270e5f
llama-cpp-classify answers match Pi bit for bit: the softmax uses jsmath.Exp, V8's fdlibm Math.exp…
- Source: PiG's own defect
- Symptom:
llama-cpp-classifyanswers match Pi bit for bit: the softmax usesjsmath.Exp, V8's fdlibmMath.exp(Go'smath.Expdiffers in the last bit for some arguments on amd64). - Root cause: D119, D125, D127, D133, D139 and D141 are retired in the text. Pages that state the 1.1.0 behaviour next to the change are recorded in version-records.toml. Output-sensitive walkthrough steps were re-run on an int-050 build: version strings, hashes and the closure-path error are updated. The llama-cpp-classify fragment no longer claims identical requests.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
3270e5f205(2026-10-13) - Tests: None recorded.
- Release note:
llama-cpp-classifyanswers match Pi bit for bit: the softmax usesjsmath.Exp, V8's fdlibmMath.exp(Go'smath.Expdiffers in the last bit for some arguments on amd64). Its requests are written with their members in Pi's order, and a failed lookup no longer cancels the sibling requests of the samePromise.all, as in Pi.
BF-32ae04c
Durable (Pi 89e5c2c0): a generation waiting on a blocking threshold compaction fails with model_error…
- Source: PiG's own defect
- Symptom: Durable (Pi 89e5c2c0): a generation waiting on a blocking threshold compaction fails with
model_error(Compaction failed: <error>) when that compaction failed, faulted, was orphaned, or was aborted directly, instead of sending a request over a context it judged too large; a compaction that found nothing to compact or was declined still lets the request go ahead. - Root cause: scenarios/storage-failure.mjs runs one conversation (a submit with a tool call, an event stream, a follow-up) on a Storage whose commit number k throws once, and prints what each caller saw: the call that hit it, later calls and waits, the stream's end, the Harness's end. 22 diff rows (gate k=2..20, node k=3,9,15) compare it with Pi 42a (Session.closed/SessionEnd, SessionFailed, harness-storage-failure.test.ts) at every level.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1595741593(2026-10-10),761ed6e951(2026-10-10),b3ba0af150(2026-10-10),15c9cc458c(2026-10-11),32ae04cc76(2026-10-11),34f7594aa5(2026-10-11),38a3abb15b(2026-10-11),a0c51e0655(2026-10-11) - Tests:
durable/contract/test/setup.test.mjs,durable/core/driver/compaction_failure_core_test.go::TestCoreAFailedBlockingCompactionFailsTheRunAndIsRecorded,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.tomland 1 more in docs/bug-ledger.json - Release note: Durable (Pi 89e5c2c0): a generation waiting on a blocking threshold compaction fails with
model_error(Compaction failed: <error>) when that compaction failed, faulted, was orphaned, or was aborted directly, instead of sending a request over a context it judged too large; a compaction that found nothing to compact or was declined still lets the request go ahead.pi.live.compactionFailure(LiveState.CompactionFailure) keeps the newest failure until a compaction produces a summary,SnapshotEvent.CompactionFailurecarries it, andCompactionEndEvent.Erroris set for a failed compaction.GenerationCheckpoint.Thresholdmarks the prepare phase that waits for the compaction.
BF-32b5367
A running !command that prints a lot, the /resume search over many long sessions, and streamed answers…
- Source: PiG's own defect
- Symptom: A running
!commandthat prints a lot, the/resumesearch over many long sessions, and streamed answers without Mermaid diagrams cost less per frame or keystroke, with the same output. - Root cause: Each keystroke in /resume's search rebuilt every session's search text (id, name, all message text, cwd) and lowered and converted it to UTF-16 for each fuzzy token, so a keystroke allocated several copies of all text of all sessions. Pi does the same (session-selector-search.ts getSessionSearchText per match, fuzzyMatch lowers the text). The selector now keeps, per listed session, the search text with the forms the query modes read: the lowercase UTF-16 units for fuzzy tokens, the whitespace-normalized lowercase text for phrases, and the unit runes for regex. Each form is prepared when a query first needs it.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
32b5367dd9(2026-10-10),9f76da76d0(2026-10-10),ab5be97bbe(2026-10-10),c4fef6c21a(2026-10-10),e892003a81(2026-10-10) - Tests:
internal/codingagent/complexity_scaling_mermaid_norace_test.go::TestScalingMermaidTransformOfAStreamedMessage,internal/codingagent/complexity_scaling_norace_test.go::TestScalingSessionSearchKeystroke,internal/codingagent/mermaid_word_test.go::TestMermaidTransformSkipsOnlyMessagesWithoutTheWord,internal/codingagent/session_search_cache_test.go::TestCachedSessionSearchEqualsAFreshSearch,internal/codingagent/session_search_cache_test.go::TestCachedSessionSearchReadsAChangedSession,tui/complexity_scaling_norace_test.go::TestScalingBashOutputFrame,tui/highlight_cache_bound_test.go::TestHighlightMemoKeepsOnlyTheNewestPrefixOfAStreamedBlock,tui/markdown_streaming_reuse_test.go::TestStreamedMarkdownEqualsAFreshRenderand 1 more in docs/bug-ledger.json - Release note: A running
!commandthat prints a lot, the/resumesearch over many long sessions, and streamed answers without Mermaid diagrams cost less per frame or keystroke, with the same output.
BF-32ce7ad
The Termux page shows the [Context] line as PiG prints it, ~/.pig/agent/AGENTS.md
- Source: PiG's own defect
- Symptom: The Termux page shows the
[Context]line as PiG prints it,~/.pig/agent/AGENTS.md. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Android and Termux
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
32ce7ad1c7(2026-10-11) - Tests: None recorded.
- Release note: The Termux page shows the
[Context]line as PiG prints it,~/.pig/agent/AGENTS.md.
BF-32e86a8
Go, Python and Rust SDK: theme.bold, italic, underline, inverse and strikethrough now draw as Pi's…
- Source: PiG's own defect
- Symptom: They wrapped the whole text once, so a multi-line or nested string lost the style past the first line or inner close, and an empty string came back as two escape sequences.
- Root cause: TestThemeQueriesAcrossSDKs reaches every remaining ctx.ui.theme member in all four SDKs and every placement (sourcePath, getColorMode, bold, italic, underline, inverse, strikethrough, bg, getFgAnsi, getBgAnsi, getThinkingBorderColor, getBashModeBorderColor) against Pi's Theme (theme.ts:242-436) over the host's palette, with modifiers on and off, both color modes, an unknown token and an unknown thinking level.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
32e86a8af3(2026-10-10) - Tests:
test/extension-conformance/theme_test.go::TestThemeQueriesAcrossSDKs - Release note: Go, Python and Rust SDK:
theme.bold,italic,underline,inverseandstrikethroughnow draw as Pi's chalk does: an empty text stays empty, a line break closes and re-opens the style, and an inner close of the same style re-opens it. Before, they wrapped the whole text once, so a multi-line or nested string lost the style past the first line or inner close, and an empty string came back as two escape sequences.
BF-334bb4b
A Gemini or Mistral stream chunk with a member of an unexpected type no longer sorts the members of a tool…
- Source: PiG's own defect
- Symptom: A Gemini or Mistral stream chunk with a member of an unexpected type no longer sorts the members of a tool call's object arguments: the call keeps the order the model wrote them in, as Pi's
JSON.parseread does. - Root cause: The Gemini, Mistral, openai-completions, Responses and Anthropic normalizers re-encoded the parsed record with json.Marshal, which sorts object members. A Gemini functionCall's args (google-generative-ai.ts:207) and Mistral object arguments (mistral-conversations.ts:744-747) then reached the tool call sorted. Encode with orderedjson.MarshalInSourceOrder against the chunk text.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
334bb4b72a(2026-10-13) - Tests:
ai/google_chunk_member_type_oracle_test.go::TestGoogleChunkMistypedMembersMatchPi,ai/mistral_chunk_member_type_oracle_test.go::TestMistralChunkMistypedMembersMatchPi - Release note: A Gemini or Mistral stream chunk with a member of an unexpected type no longer sorts the members of a tool call's object arguments: the call keeps the order the model wrote them in, as Pi's
JSON.parseread does.
BF-3351167
The OAuth callback server reads an absolute-form request target with a scheme other than http or https, a…
- Source: PiG's own defect
- Symptom: The OAuth callback server reads an absolute-form request target with a scheme other than http or https, a relative reference against a base that has a path and query, and a
\path the way Node'snew URL(request.url, base)does, instead of falling back to Go'snet/url. - Root cause: The URL expression of callback-server.ts:80 (new URL(request.url ?? '/', 'http://localhost')) runs in Node (testdata/resolve_request.mjs) on 5000 seeded request targets and eight bases with paths, queries and fragments: origin-form paths with dot segments, encoded dots, backslashes, repeated slashes, queries of every shape, absolute-form targets with other schemes, '*' and authority-looking text. ResolveURL is the same relative-resolution state machine over ParseURL and ResolveRelativePath, for any scheme in the input and a special base; ResolveHTTPURL is gone, and requestPathAndQuery loses its net/url fallback.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2c04c1445a(2026-10-09),31f4517fbe(2026-10-09),335116710e(2026-10-09),d1cbde3321(2026-10-09),d80c1cd150(2026-10-09),da8a8704c6(2026-10-09) - Tests:
ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,ai/chatgpt_manual_input_oracle_test.go::TestChatGPTManualInputMatchesPiOnSeededProbes,ai/radius_urls_oracle_test.go::TestRadiusURLsMatchPiOnSeededProbes,ai/service_urls_oracle_test.go::TestProviderServiceURLsMatchPiOnSeededProbes,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,internal/nodeurl/reference_test.go::TestResolveURLMatchesNodeURL,internal/nodeurl/resolve_request_oracle_test.go::TestResolveURLRequestTargetsMatchNode,internal/nodeurl/url_oracle_test.go::TestParseMatchesNodeURL - Release note: The OAuth callback server reads an absolute-form request target with a scheme other than http or https, a relative reference against a base that has a path and query, and a
\path the way Node'snew URL(request.url, base)does, instead of falling back to Go'snet/url.
BF-33dc8a0
fix(ai): keep a custom client identity's slash in Bedrock's user agent, as the JavaScript SDK does
- Source: PiG's own defect
- Symptom: fix(ai): keep a custom client identity's slash in Bedrock's user agent, as the JavaScript SDK does
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
33dc8a0a22(2026-10-21) - Tests:
ai/identity_bedrock_oracle_test.go::TestBedrockUserAgentMatchesPi,ai/identity_bedrock_test.go::TestBedrockUserAgentCarriesTheClientIdentity
BF-33ea9e4
fix(sdk): name the Go ModelRegistry.Find change in the upgrade rules; Node adoption survives an unbound leaf…
- Source: PiG's own defect
- Symptom: fix(sdk): name the Go ModelRegistry.Find change in the upgrade rules; Node adoption survives an unbound leaf read
- Root cause: The Go Find signature change is an incompatible API change: add its upgrade rule, classify the compiler's arity diagnostic for it and list the Go, Rust and Python changes under Upgrading.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
33ea9e4573(2026-10-20) - Tests:
extensions/sdk/upgrade/rules_test.go::TestModelRegistryFindChangeIsNamed
BF-33fe10d
ModelsError appends the cause detail the way Pi does: an Error cause with an empty message contributes its…
- Source: PiG's own defect
- Symptom:
ModelsErrorappends the cause detail the way Pi does: an Error cause with an empty message contributes its name ("load failed: TypeError") instead of nothing (ai/auth_resolve.goNewModelsError, models-error.ts withCauseDetail; live-Pi oracleTestModelsErrorMessageMatchesPi, 3,000 probes, 147 red before; three mutations red). - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),33fe10d58e(2026-10-11),c4ad739eb8(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/models_error_oracle_test.go::TestModelsErrorMessageMatchesPi,coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundariesand 3 more in docs/bug-ledger.json - Release note:
ModelsErrorappends the cause detail the way Pi does: an Error cause with an empty message contributes its name ("load failed: TypeError") instead of nothing (ai/auth_resolve.goNewModelsError, models-error.ts withCauseDetail; live-Pi oracleTestModelsErrorMessageMatchesPi, 3,000 probes, 147 red before; three mutations red).
BF-33fe10d-2
Live-Pi oracles TestExtensionSelectorMatchesPi (4,320 probes) and TestExtensionInputMatchesPi (1,144…
- Source: PiG's own defect
- Symptom: Live-Pi oracles
TestExtensionSelectorMatchesPi(4,320 probes) andTestExtensionInputMatchesPi(1,144 probes) (tui/extension_selector_oracle_test.go,tui/extension_input_oracle_test.go): every frame and callback after each key against the pinned Pi component, default and rebound select keys, descriptions, wide/empty content, focus; no bug found; six and seven mutations red (the lone-LF confirm needed its own probe). - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),33fe10d58e(2026-10-11),c4ad739eb8(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/models_error_oracle_test.go::TestModelsErrorMessageMatchesPi,coding/discover_extensions_oracle_test.go::TestDiscoverExtensionPathsMatchPi,coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPiand 10 more in docs/bug-ledger.json - Release note: Live-Pi oracles
TestExtensionSelectorMatchesPi(4,320 probes) andTestExtensionInputMatchesPi(1,144 probes) (tui/extension_selector_oracle_test.go,tui/extension_input_oracle_test.go): every frame and callback after each key against the pinned Pi component, default and rebound select keys, descriptions, wide/empty content, focus; no bug found; six and seven mutations red (the lone-LF confirm needed its own probe).
BF-3452432
Escape cancels a running ! or !! command again, as in Pi: the command stops and its block ends with…
- Source: PiG's own defect
- Symptom: Escape cancels a running
!or!!command again, as in Pi: the command stops and its block ends with(cancelled). - Root cause: TestTaskAbort's abort-again case let the caller join before the run phase started, so under load the caller's cancel raced the task settling and the waiter returned nil instead of the cancel cause. Pi's scheduler joins only a run invocation already on the line (scheduler.ts:274-296); the test now waits for the run phase first.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a4753ec61e(2026-10-06, #170),ba9c37b247(2026-10-06, #171),3094ee00ca(2026-10-07, #185),3499c2bea7(2026-10-07, #181),9b63b2bc4e(2026-10-07, #176),3452432f8b(2026-10-08, #190),4610c9f4a1(2026-10-08, #189) - Tests:
coding/cli/rpc_ctx_signal_test.go::TestExtensionContextSignalComparedWithPi,coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleBusyEntriesAreSkippedNotErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledBeforeStartTouchesNothing,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledDuringTheLastEntryReportsTheCancellation,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleEntryLeasedAfterClassificationIsSkippedNotAnError,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleOtherErrorsStayErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleStopsWhenItsContextIsCancelledand 41 more in docs/bug-ledger.json - Release note: Escape cancels a running
!or!!command again, as in Pi: the command stops and its block ends with(cancelled). Escape had left the command running until it finished on its own. While an agent run streams, Escape still aborts the run and leaves a command started during it running, as Pi does.
BF-34aa421
fix(agent): hand the stream function Pi's messages; repair the tool flow in the provider path
- Source: PiG's own defect
- Symptom: fix(agent): hand the stream function Pi's messages; repair the tool flow in the provider path
- Root cause: The agent loop's default conversion ran NormalizeMessages, so a custom stream function received a request Pi never builds: errored and aborted turns dropped, a synthetic 'No result provided' result for each orphaned call, orphaned/repeated results stripped, thinking rewritten (agent-loop.ts streamAssistantResponse calls convertToLlm only; the transformMessages pass lives in each provider, transform-messages.ts:36-190).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
34aa4214e8(2026-10-10) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,agent/transform_provider_path_test.go::TestPoisonedHistoryReachesProviderLikePiWithoutRepair,ai/tool_flow_repair_test.go::TestRepairToolFlow,ai/tool_flow_repair_test.go::TestRepairToolFlowKeepsAWellFormedTranscript
BF-34b8e92
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
34b8e92a21(2026-10-21) - Tests:
coding/extension/host/subprocess/tool_window_settled_test.go::TestNodeToolWithoutStartWindowTakesTheFileMutationQueueAfterAwaiting
BF-34bcd58
OpenAI Responses prices a response that reports no service_tier at the requested tier, as Pi's…
- Source: PiG's own defect
- Symptom: OpenAI Responses prices a response that reports no
service_tierat the requested tier, as Pi'sprocessResponsesStreamdoes (response.service_tier ?? options.serviceTier); only Codex did before, so a flex or priority request to OpenAI Responses was billed at the default rate. - Root cause: Pi openai-responses-shared.ts:110-122 OpenAIResponsesStreamOptions and :433 processResponsesStream, :578-583 tier pricing, :600 onProviderStreamEvent. - ai/openai_responses.go: the body of parseResponses is now the free function processResponsesStream(ctx, decoder, builder, *OpenAIResponsesStreamOptions); parseResponses builds the options (Codex gets resolveCodexServiceTier from openai-codex-responses.ts:631-639, Azure passes no pricing as azure-openai-responses.ts:97). Codex-only stream handling that Pi keeps outside the shared function stays in an unexported transport field. - Bug: non-Codex Responses priced a response without service_tier at the default rate; Pi falls back to…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
97051b184f(2026-10-05),34bcd58204(2026-10-07),77bfe68d1f(2026-10-07),d5d010a275(2026-10-07),db7007dad8(2026-10-07) - Tests:
ai/constrained_sampling_upstream_test.go::TestConstrainedSamplingConversionUpstream,ai/openai_responses_foreign_toolcall_test.go::TestResponsesConvertMessages_ForeignCopilotToolCallIDHashedToFcShape,ai/openai_responses_foreign_toolcall_test.go::TestResponsesToolCallID_UnattributedItemIsHashed,ai/openai_responses_shared_oracle_test.go::TestConvertResponsesMessagesMatchesPi,ai/openai_responses_stream_options_test.go::TestProcessResponsesStreamCallsTheInjectedOptions,ai/openai_responses_stream_options_test.go::TestResolveCodexServiceTier,ai/openai_responses_stream_options_test.go::TestResponsesPricesAResponseWithoutServiceTierAtTheRequestedTier,ai/openai_responses_test.go::TestNormalizeResponsesToolCallIDProviderSetsand 11 more in docs/bug-ledger.json - Release note: OpenAI Responses prices a response that reports no
service_tierat the requested tier, as Pi'sprocessResponsesStreamdoes (response.service_tier ?? options.serviceTier); only Codex did before, so a flex or priority request to OpenAI Responses was billed at the default rate.
BF-34c4d8e
MCP tools declare the CallToolResult scripts receive as Pi 1.2.0 does: content blocks with their type…
- Source: PiG's own defect
- Symptom:
describeTool()and theCodemode:line of a directly exposed MCP tool listed_meta?, left outstructuredContentwithout an output schema, and lost the types behind such references (Pi 1.2.0mcp/tools.tscreateMcpResultSchema, #10416). - Root cause: Activation and the tool_call wait use ToolInfo.ReachesTools instead of the IsCodemodeTool/IsToolSearchTool source-path checks, which are removed. The fake host's codemode and tool_search stand-ins carry the built-in tools' ReachesTools.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34c4d8e058(2026-10-12),8217a5923f(2026-10-12) - Tests:
coding/mcpext/regression_test.go::TestCreateMcpToolDefinitionMatchesUpstreamProbes,coding/mcpext/tools_test.go::TestMCPToolsDeclaresTheCallToolResultScriptsReceiveKeepingReferencesInTheServersOutputSchema - Release note: MCP tools declare the
CallToolResultscripts receive as Pi 1.2.0 does: content blocks with theirtype,text,dataandmimeTypefields,structuredContentalways (typedunknownwhen the server declares no output schema), no_meta, and room for fields newer protocol versions add. Local$refs in a server's output schema are rewritten to point belowstructuredContent, so they resolve to the same definitions. Before,describeTool()and theCodemode:line of a directly exposed MCP tool listed_meta?, left outstructuredContentwithout an output schema, and lost the types behind such references (Pi 1.2.0mcp/tools.tscreateMcpResultSchema, #10416).
BF-34c4d8e-2
The MCP extension decides which tools reach MCP tools by their ReachesTools, as Pi 1.2.0 does: another…
- Source: PiG's own defect
- Symptom: Only the built-in tools of those names were recognized, by their source path (Pi 1.2.0
mcp/index.ts). - Root cause: Activation and the tool_call wait use ToolInfo.ReachesTools instead of the IsCodemodeTool/IsToolSearchTool source-path checks, which are removed. The fake host's codemode and tool_search stand-ins carry the built-in tools' ReachesTools.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34c4d8e058(2026-10-12),8217a5923f(2026-10-12) - Tests:
coding/mcpext/regression_test.go::TestCreateMcpToolDefinitionMatchesUpstreamProbes,coding/mcpext/tools_test.go::TestMCPToolsDeclaresTheCallToolResultScriptsReceiveKeepingReferencesInTheServersOutputSchema - Release note: The MCP extension decides which tools reach MCP tools by their
ReachesTools, as Pi 1.2.0 does: another extension'scodemodeortool_searchthat declaresreachesToolsis activated for MCP servers and waits for the servers its call may reach. Before, only the built-in tools of those names were recognized, by their source path (Pi 1.2.0mcp/index.ts).
BF-34f7594
An extension that calls ctx.shutdown() from a session_start handler, or from a session_before_switch or…
- Source: PiG's own defect
- Symptom: An extension that calls
ctx.shutdown()from asession_starthandler, or from asession_before_switchorsession_before_forkhandler during/new,/resumeor/fork, ends the session as Pi does: extensions getsession_shutdown,pigprints the resume command when the session has a file, and exits 0 without waiting for the handler that asked for the shutdown. - Root cause: Pi's shutdown() stops the TUI, awaits runtimeHost.dispose() (session_shutdown), prints the resume command when the session file exists and calls process.exit(0); it never awaits the session_start, session_before_switch or session_before_fork handler that asked for the shutdown (interactive-mode.ts:2020-2024, 4251-4290).
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11) - Tests:
internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-start.toml - Release note: An extension that calls
ctx.shutdown()from asession_starthandler, or from asession_before_switchorsession_before_forkhandler during/new,/resumeor/fork, ends the session as Pi does: extensions getsession_shutdown,pigprints the resume command when the session has a file, and exits 0 without waiting for the handler that asked for the shutdown.pighad restored the terminal and then waited forever for that handler, and/newexited 1 as if creating the session had failed.
BF-34f7594-2
Fixed the interactive bash block stripping escape sequences with the wrong rule…
- Source: PiG's own defect
- Symptom: Fixed the interactive bash block stripping escape sequences with the wrong rule:
BashExecutionComponent.AppendOutputnow removes ANSI like Pi'sstripAnsi(ansi-regex), so an unfinishedESC[38;5;loses onlyESC[38;5and a digit ends a sequence, instead of using the terminal-width scanner's rule. - Root cause: Pi's shutdown() stops the TUI, awaits runtimeHost.dispose() (session_shutdown), prints the resume command when the session file exists and calls process.exit(0); it never awaits the session_start, session_before_switch or session_before_fork handler that asked for the shutdown (interactive-mode.ts:2020-2024, 4251-4290).
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11) - Tests:
internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-start.toml - Release note: Fixed the interactive bash block stripping escape sequences with the wrong rule:
BashExecutionComponent.AppendOutputnow removes ANSI like Pi'sstripAnsi(ansi-regex), so an unfinishedESC[38;5;loses onlyESC[38;5and a digit ends a sequence, instead of using the terminal-width scanner's rule. The regular expression moved tointernal/text.StripANSI, which the tool output path shares.
BF-34f7594-3
Fixed the re: mode of the session selector search to read its pattern as JavaScript does (`new…
- Source: PiG's own defect
- Symptom: Fixed the
re:mode of the session selector search to read its pattern as JavaScript does (new RegExp(pattern, "i")): assertions that cannot be repeated (\b{2},^*, a quantified lookbehind) and class ranges with a class escape ([a-\d]) now give Pi's results, a pattern is matched as UTF-16 code units so a quantifier binds to the second half of an astral character,\b,\wand\suse JavaScript's ASCII and WhiteSpace sets, case-insensitive matching uses JavaScript's case mapping (İno longer matchesi),(?s:and(?m:modifier groups are accepted, and an invalid pattern reports V8's message (Invalid regular expression: /…/i: Nothing to repeat). - Root cause: Pi's shutdown() stops the TUI, awaits runtimeHost.dispose() (session_shutdown), prints the resume command when the session file exists and calls process.exit(0); it never awaits the session_start, session_before_switch or session_before_fork handler that asked for the shutdown (interactive-mode.ts:2020-2024, 4251-4290).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11) - Tests:
internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-start.toml - Release note: Fixed the
re:mode of the session selector search to read its pattern as JavaScript does (new RegExp(pattern, "i")): assertions that cannot be repeated (\b{2},^*, a quantified lookbehind) and class ranges with a class escape ([a-\d]) now give Pi's results, a pattern is matched as UTF-16 code units so a quantifier binds to the second half of an astral character,\b,\wand\suse JavaScript's ASCII and WhiteSpace sets, case-insensitive matching uses JavaScript's case mapping (İno longer matchesi),(?s:and(?m:modifier groups are accepted, and an invalid pattern reports V8's message (Invalid regular expression: /…/i: Nothing to repeat).
BF-34f7594-4
Repeated codemode timeouts or aborts can no longer pile up running VMs: at most two codemode VMs are alive…
- Source: PiG's own defect
- Symptom: Repeated codemode timeouts or aborts can no longer pile up running VMs: at most two codemode VMs are alive across sessions in one process, and a cancelled VM holds its slot until it exits (#10748).
- Root cause: Pi's shutdown() stops the TUI, awaits runtimeHost.dispose() (session_shutdown), prints the resume command when the session file exists and calls process.exit(0); it never awaits the session_start, session_before_switch or session_before_fork handler that asked for the shutdown (interactive-mode.ts:2020-2024, 4251-4290).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11) - Tests:
internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-start.toml - Release note: Repeated codemode timeouts or aborts can no longer pile up running VMs: at most two codemode VMs are alive across sessions in one process, and a cancelled VM holds its slot until it exits (#10748).
BF-3548f29
fix(tui): bash block strips ANSI with Pi's utils/ansi.ts rule
- Source: PiG's own defect
- Symptom: fix(tui): bash block strips ANSI with Pi's utils/ansi.ts rule
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
3548f29c38(2026-10-10) - Tests:
internal/codingagent/bash_execution_oracle_test.go::TestBashExecutionComponentMatchesPi
BF-35e46b1
Codemode output and returned values keep a nested Error's name and message, with its own enumerable fields…
- Source: A Pi defect that PiG fixed first (divergence D145)
- Symptom: Codemode output and returned values keep a nested Error's name and message, with its own enumerable fields, instead of
{}(D145, Pi #10650). - Root cause: D142 (coding/session_run_state.go): runningDeferred was a bool that the nested runDeferredSettledActions of the first deferred run cleared, so a waiter registered during the run resolved while a second deferred prompt was in its preflight (order request:1 queued:a queued:b request:2 idle-resolved request:3). It is now a batch count. Pi: agent-session.ts:1077-1099 _emitAgentSettled, :2434-2451 abort/waitForIdle, :2764-2765 compact awaits abort.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10650
- Removed when Pi fixes it: Pi's codemode serializes nested Errors with their name and message.
- Fix commits:
0225a43aa5(2026-10-10),35e46b114e(2026-10-10),bd4c4d7f15(2026-10-10),39fc7f1aea(2026-10-11) - Tests:
codemode/nested_error_console_test.go::TestNestedErrorsKeepTheirMessageInConsoleOutputAndMixedResults,codemode/nested_error_output_test.go::TestNestedErrorsKeepTheirEnumerableFields,codemode/nested_error_output_test.go::TestNestedToolErrorsKeepTheirMessageInSerializedOutput,codemode/nested_error_unreadable_test.go::TestNestedErrorsWithUnreadableNamesSerializeAsPiDoes,codemode/sandbox_test.go::TestEmbeddedSourcesMatchTheirRecordedHashes,coding/session_settled_idle_test.go::TestAbortDuringADeferredContinuationDiscardsTheRest,coding/session_settled_idle_test.go::TestCustomMessageRunSettledHandlerAwaitsCompaction,coding/session_settled_idle_test.go::TestWaitForIdleStaysPendingThroughEveryDeferredPromptand 1 more in docs/bug-ledger.json - Release note: Codemode output and returned values keep a nested Error's name and message, with its own enumerable fields, instead of
{}(D145, Pi #10650).
BF-364cc57
RPC model objects of built-in models match Pi's: each keeps its catalog entry's own member order and spelling…
- Source: PiG's own defect
- Symptom: RPC model objects of built-in models match Pi's: each keeps its catalog entry's own member order and spelling (
"type"last for most models, first for others;inputLimits.imagesas the entry spells it), and amodels.jsonoverride keeps the changed members in place, with merged objects listing the catalog's members first. - Root cause: Pi's RPC model objects are its catalog's model objects, so each is JSON.stringify of its MODELS entry: "type" is last in 1164 chat entries and first in 399, baseUrl precedes provider in some, and inputLimits.images lists maxPerRequest first. PiG wrote every model in one fixed RPCModel order.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
364cc57510(2026-10-10) - Tests:
ai/catalog_model_json_oracle_test.go::TestCatalogModelJSONIsAbsentWithoutARecord,ai/catalog_model_json_oracle_test.go::TestCatalogModelJSONMatchesPiForEveryBuiltinModel,coding/cli/rpc_model_type_binary_test.go::TestRPCModelObjectsCarryTheRegistryModelType,coding/cli/rpc_model_type_binary_test.go::TestRPCOverriddenBuiltinModelKeepsTheCatalogOrder - Release note: RPC model objects of built-in models match Pi's: each keeps its catalog entry's own member order and spelling (
"type"last for most models, first for others;inputLimits.imagesas the entry spells it), and amodels.jsonoverride keeps the changed members in place, with merged objects listing the catalog's members first.
BF-3651a90
A provider stream that keeps sending keepalive comments or pings but no event now ends with an error after…
- Source: A Pi defect that PiG fixed first (divergence D131)
- Symptom: A provider stream that keeps sending keepalive comments or pings but no event now ends with an error after
httpIdleTimeoutMsinstead of hanging for ever; the timeout restarts on each event, and on keepalives while the model is reasoning (D131, pi-mono #10761, #10762). - Root cause: sseIdleBody.scan ended a line at CR and again at the LF after it, so the empty line between them reset the event's ping flag: a CRLF stream's ping event counted as progress and kept a stalled stream alive for ever, the hang D131 fixes. A CR followed by LF is now one line end, and the SSE field value drops only the single space the grammar allows. New rows: ping events with CRLF and with CR line ends (the CRLF row is red without the fix).
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10761
- Removed when Pi fixes it: Pi ends a stream that sends no event for
httpIdleTimeoutMs. - Fix commits:
00c8373052(2026-10-10),3651a9096a(2026-10-10),4b6ef3e320(2026-10-10),64461c2028(2026-10-10),b9e2b3fb9b(2026-10-10) - Tests:
ai/sse_event_idle_test.go::TestSSEIdleTrackerRecognizesReasoningEvents,ai/sse_event_idle_test.go::TestSSEKeepalivesAfterANonReasoningEventStillEndTheStream,ai/sse_event_idle_test.go::TestSSEKeepalivesAfterAReasoningEventDoNotEndTheStream,ai/sse_event_idle_test.go::TestSSEStreamWithOnlyKeepalivesEndsAfterTheIdleTimeout,ai/sse_event_idle_test.go::TestSSEStreamWithSlowEventsIsNotCutOff - Release note: A provider stream that keeps sending keepalive comments or pings but no event now ends with an error after
httpIdleTimeoutMsinstead of hanging for ever; the timeout restarts on each event, and on keepalives while the model is reasoning (D131, pi-mono #10761, #10762).
BF-369b1e1
grep with context no longer loads each matched file into memory
- Source: PiG's own defect
- Symptom: A search over a directory of very large logs used to exhaust the heap; the output is unchanged (Pi #9276).
- Root cause: grep.ts getFileLines reads and splits every matched file; PiG ported it. Output unchanged; heap growth for a 50 MB file with context 2 drops from 150 MB to under 12 MB.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
369b1e1ce3(2026-10-09) - Tests:
internal/codingagent/tools/grep_context_memory_test.go::TestGrepContextDoesNotLoadTheMatchedFile,internal/codingagent/tools/grep_context_memory_test.go::TestGrepContextOfUnreadableFile,internal/codingagent/tools/grep_context_memory_test.go::TestGrepLineWindowMatchesTheWholeFileSplit - Release note:
grepwithcontextno longer loads each matched file into memory. A search over a directory of very large logs used to exhaust the heap; the output is unchanged (Pi #9276).
BF-36e192b
fix(lint): port Kitty chunking in UTF-16 units; clear the codemode, terminal image and exit-restore lint hits
- Source: PiG's own defect
- Symptom: fix(lint): port Kitty chunking in UTF-16 units; clear the codemode, terminal image and exit-restore lint hits
- Root cause: EncodeKitty measured and sliced the payload in bytes where Pi's encodeKitty uses .length and .slice (UTF-16 units). EncodeITerm2's size is Node's (units * 3) >>> 2. The codemode worker wait is computed in integers, and the doubleclose, numbers, env-stat and bare-recover sites name their Pi counterpart or invariant.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
36e192bef8(2026-10-21) - Tests:
tui/terminal_image_iterm2_options_test.go::TestEncodeKittyChunksByUTF16Units
BF-36e82ad
fix(durable): a non-finite number is a strict JSON error whichever JSON package a value's own encoder used
- Source: PiG's own defect
- Symptom: fix(durable): a non-finite number is a strict JSON error whichever JSON package a value's own encoder used
- Root cause: Fix-Commit:
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
36e82ade57(2026-10-24) - Tests: None recorded.
BF-37c6048
fix(tui): a streamed Markdown never resumes past a paragraph clipped before a LaTeX start
- Source: PiG's own defect
- Symptom: fix(tui): a streamed Markdown never resumes past a paragraph clipped before a LaTeX start
- Root cause: renderBlocks clips a top-level paragraph where "$$" or "[" follows its first character (Lexer.ts:241-270 with pi-tui's latexBlock start()) and lexes the rest of the line again. resumeBarrier only knew a LaTeX start at the beginning of a line, so (a) a later "$$" that closes the pending block changed blocks before a recorded point, and (b) the line visited again added a second point at the same line whose kept output already held the clipped character, which a later resume rendered twice ("x" twice for "\nx$$$$$\n\n1. c- a$$* b> \n "). latexClipStart makes such a line a barrier like a line-start LaTeX block.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
37c60480c3(2026-10-10) - Tests:
tui/markdown_resume_soup_test.go::TestResumedAssistantBlockEqualsAFreshBlock,tui/markdown_resume_soup_test.go::TestResumedMarkdownSoupEqualsAFreshRender,tui/markdown_resume_test.go::TestResumedMarkdownEqualsAFreshRenderForDependentBlocks
BF-37d2daa
An extension's ctx.model and model lists keep a model's cost.tiers when the model's record has it as an…
- Source: PiG's own defect
- Symptom: An extension's
ctx.modeland model lists keep a model'scost.tierswhen the model's record has it as an empty array (a provider object's or a models store's model), as Pi keeps the authored cost, instead of dropping the member. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
37d2daad81(2026-10-13) - Tests: None recorded.
- Release note: An extension's
ctx.modeland model lists keep a model'scost.tierswhen the model's record has it as an empty array (a provider object's or a models store's model), as Pi keeps the authored cost, instead of dropping the member.
BF-3804a5a
The pi-messages and classifier (System One, decisions) requests are sent to the URL as written and a URL with…
- Source: PiG's own defect
- Symptom: The pi-messages and classifier (System One, decisions) requests are sent to the URL as written and a URL with credentials is refused as fetch refuses it, where Pig sent the credentials as Basic auth.
- Root cause: pi-messages.ts:370,392 hands new URL(...) to options.fetch ?? globalThis.fetch, classifier-shared.ts:73 the target to the same; fetch sends the serialized URL as is and throws a TypeError naming it for a URL with a username or password. Pig re-parsed the href with net/url and sent credentials as Basic auth. Both now use ai.newFetchRequest.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
3804a5a2ab(2026-10-10) - Tests:
ai/sdk_request_url_oracle_test.go::TestPostClassifierRequestRefusesCredentialsLikeFetch,ai/sdk_request_url_oracle_test.go::TestSDKProvidersRequestURLMatchesSDK - Release note: The pi-messages and classifier (System One, decisions) requests are sent to the URL as written and a URL with credentials is refused as fetch refuses it, where Pig sent the credentials as Basic auth.
BF-3840820
MCP tool results whose content blocks carry members of another type than the protocol's (a numeric text, a…
- Source: PiG's own defect
- Symptom: MCP tool results whose content blocks carry members of another type than the protocol's (a numeric
text, asizestring, a missinguri, anullblock, aresourcethat is not an object) now convert as Pi converts them: the model sees what Pi's would, and a block Pi cannot read ends the tool call with Pi's error instead of a placeholder. - Root cause: package_updates.go, package_update_operations.go, package_npm_metadata.go and the update source matching move from cmd/pig into internal/packagemanager. PackageManager.Update(source) and PackageManager.CheckForAvailableUpdates() are Pi's update and checkForAvailableUpdates; cmd/pig update and the startup update check call them. Pure update tests (operations, semver/metadata, capture) move with the code; the CLI-fixture tests stay in cmd/pig and call the exported members. Behaviour is unchanged (cmd/pig package/update/npm/git tests pass).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a79de03e7f(2026-10-07),d0ad06fc18(2026-10-07),3840820cd6(2026-10-08),ab8ac8ecfc(2026-10-08) - Tests:
coding/cli/package_update_cases_upstream_test.go::TestPackageUpdateLookupPrefixesOriginal,coding/mcpext/tools_test.go::TestMCPToolsKeepAnExplicitEmptyTitle,coding/mcpext/tools_test.go::TestMCPToolsShowsBlobsOfTextTypesWithBOMPaddedMimeTypesAsText,internal/packagemanager/package_capture_test.go::TestPackageCaptureChild,internal/packagemanager/package_capture_test.go::TestPackageCaptureOutputAndFailure,internal/packagemanager/package_capture_test.go::TestPackageCaptureStartFailure,internal/packagemanager/package_capture_test.go::TestPackageCaptureTimeoutWaitsForChild,internal/packagemanager/package_manager_test.go::TestPackageManagerPersistsInstallAndRemovaland 6 more in docs/bug-ledger.json - Release note: MCP tool results whose content blocks carry members of another type than the protocol's (a numeric
text, asizestring, a missinguri, anullblock, aresourcethat is not an object) now convert as Pi converts them: the model sees what Pi's would, and a block Pi cannot read ends the tool call with Pi's error instead of a placeholder.
BF-38b0f0c
Syntax highlighting keeps its colors after the first line of multiline strings and comments in fenced code…
- Source: PiG's own defect
- Symptom: Syntax highlighting keeps its colors after the first line of multiline strings and comments in fenced code blocks.
- Root cause: renderHighlightedHtml formats each non-empty line of a scoped run on its own, and the CLI highlight theme colors string substitutions with the text color (utils/syntax-highlight.ts, theme.ts, #10143).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
38b0f0c036(2026-10-05) - Tests:
tui/highlight_test.go::TestCliHighlightThemeColorsSubstitutionsWithTheTextColor,tui/highlight_test.go::TestHighlightCode_ColorsEachLineOfPythonDocstringsIndependently,tui/syntax_highlight_upstream_test.go::TestSyntaxHighlightInterpolationEndsWithTheString,tui/syntax_highlight_upstream_test.go::TestSyntaxHighlightInterpolationTakesTheTextColor - Release note: Syntax highlighting keeps its colors after the first line of multiline strings and comments in fenced code blocks.
BF-397866f
A Go MCP client that declares an empty capability, such as sampling or elicitation, now sends it as {}
- Source: PiG's own defect
- Symptom: The capability used to be left out of
initialize, so a server could not see that the client supports it. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
397866f7f2(2026-10-06) - Tests:
mcp/client_options_test.go::TestClientInitializeCarriesTheConfiguredClientOptions - Release note: A Go MCP client that declares an empty capability, such as
samplingorelicitation, now sends it as{}. The capability used to be left out ofinitialize, so a server could not see that the client supports it.
BF-3a0d0e1
A Provider object that an extension registers while it loads is now applied after every extension has…
- Source: PiG's own defect
- Symptom: A
Providerobject that an extension registers while it loads is now applied after every extension has loaded, like a provider configuration, instead of at once. - Root cause: Pi's loader queues a Provider object as it queues a configuration, and its model runtime stores nothing of a registration it refuses. The host now queues a Provider object declared while an extension loads, whatever registry is bound, and a registration the registry refuses when the queue drains gives back what the host recorded at load: the replica record, the claim on the provider's callbacks, the bridged OAuth login, a Provider object's proxy. A superseded owner is told only when the registry accepts. A scenario reloads an extension whose registration is rejected and compares with Pi.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
3a0d0e1711(2026-10-25) - Tests:
coding/cli/extension_host_wiring_guard_test.go::TestBuildFlushesTheExtensionHostProviderRegistrations,coding/extension/host/subprocess/native_provider_callback_context_test.go::TestNodeNativeProviderReceivesTheCallersOnResponseAndOnPayloadByName,coding/extension/host/subprocess/native_provider_oauth_test.go::TestNativeProviderOAuthRefreshAndLogin,coding/extension/host/subprocess/provider_load_rejection_rollback_test.go::TestRejectedLoadRegistrationLeavesAnEarlierRegistrationIntact,coding/extension/host/subprocess/provider_object_load_queue_test.go::TestProviderObjectRegisteredWhileLoadingIsQueuedForABoundRegistry,test/parity/scenarios/extensions-runtime/89-reload-provider-registration-failure.toml - Release note: A
Providerobject that an extension registers while it loads is now applied after every extension has loaded, like a provider configuration, instead of at once. When the model registry rejects a registration, PiG now leaves nothing of it behind: the login it declared, its entry in the model list that extensions read, and its claim on the provider are given back, so a registration of the same provider by another extension stays as it was. Pi's registry validates a registration before it stores anything of it.
BF-3ad9ec8
fix(tui): tree label prompt gives its input width-2 cells (Pi LabelInput)
- Source: PiG's own defect
- Symptom: fix(tui): tree label prompt gives its input width-2 cells (Pi LabelInput)
- Root cause: tree-selector.ts LabelInput.render passes availableWidth = width - 2 straight to Input.render; Pig clamped it to 1, so a two-column terminal drew one cell more.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
3ad9ec876a(2026-10-11) - Tests:
internal/codingagent/scoped_models_oracle_test.go::TestScopedModelsSelectorMatchesPi,internal/codingagent/session_selector_oracle_test.go::TestSessionSelectorInputMatchesPi,internal/codingagent/tree_selector_oracle_test.go::TestTreeSelectorInputMatchesPi,tui/model_selector_oracle_test.go::TestModelSelectorInputMatchesPi
BF-3b009a3
chord/host: a child host forked by ForkDriver no longer inherits its parent's CHORD2_LOCK
- Source: PiG's own defect
- Symptom: A child host that forked its own children passed its lock on, and children without a lock of their own failed to start with
locked(Pi a4d040d3). - Root cause: ForkDriver passed os.Environ() to the child, so a child host that forks its own children (or a test run inside a worker) passed its lock on, and children without a lock of their own failed with locked. The child's environment drops an inherited CHORD2_LOCK before the driver's env and lock apply, as Pi's forkDriver now does. TestHostManagerAChildWithoutALockDoesNotInheritTheParentsLock ports the new manager.test.ts case.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
3b009a35c1(2026-10-13),488a47a47d(2026-10-13),b02876b1d5(2026-10-13) - Tests:
chord/host/link_upstream_test.go::TestLinksARouteThatReturnsAnAlreadyClosedLinkDoesNotPoisonTheHostName,chord/host/link_upstream_test.go::TestLinksAnAlreadyClosedLinkIsNotKeptAndStartsNoPinger,chord/host/manager_upstream_test.go::TestHostManagerAChildWithoutALockDoesNotInheritTheParentsLock,chord/host/manager_upstream_test.go::TestProcessLock,chord/host/manager_upstream_test.go::TestProcessLockAdmitsOneHolderWhenTheTemporaryDirectoryIsTooDeepForASocketPath,chord/host/manager_worker_unix_test.go::TestLockSocketPathKeepsTheSocketPathWithinSunPath - Release note:
chord/host: a child host forked byForkDriverno longer inherits its parent'sCHORD2_LOCK. Before, a child host that forked its own children passed its lock on, and children without a lock of their own failed to start withlocked(Pi a4d040d3).
BF-3b3dba6
A streamed tool argument of -0 is held as 0, so the serialized arguments write 0 as Pi's…
- Source: PiG's own defect
- Symptom: A streamed tool argument of
-0is held as0, so the serialized arguments write0as Pi'sJSON.stringifydoes instead of-0. - Root cause: Comparing ToolCall.ArgumentsJSON token by token with Pi's JSON.stringify(parseStreamingJson(text)) shows member order matches on all 1504 object results, but -0 differs (48 results): Pi writes 0 (JSON.stringify, as String(-0) is "0"), Go's encoding/json writes -0. nonFiniteToNull now maps -0 to 0, on the strict path as well.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
3b3dba64c8(2026-10-09) - Tests:
ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput - Release note: A streamed tool argument of
-0is held as0, so the serialized arguments write0as Pi'sJSON.stringifydoes instead of-0.
BF-3b8aa66
A Piglet Binary that strips bedrock-converse-stream links the AWS SDK again: the Bedrock retry backoff…
- Source: PiG's own defect
- Symptom: A Piglet Binary that strips
bedrock-converse-streamlinks the AWS SDK again: the Bedrock retry backoff (ai/bedrock_retry_backoff.go) had nopig_strip_bedrock_converse_streambuild tag and importedaws-sdk-go-v2(27 packages). - Root cause: An all-strip Piglet Binary still linked go/types, go/importer and friends (965 KB of symbols) because coding/cli's extension upgrade analysis calls sdk/upgrade.Open, and that analysis needs the Go SDK the Piglet stripped. analyzeExtension moves to extension_upgrade_analyze.go (stock) with a pig_strip_extension_sdk_go variant that reports the strip (D92); the linker then drops the type checker: go/* symbols 965 KB -> 272 KB, Binary 56.10 -> 53.69 MB before -s -w.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
3b8aa66c74(2026-10-11) - Tests:
coding/cli/extension_upgrade_analyze_off_test.go::TestUpgradeInAGoSDKStrippedBinaryNamesTheStrip,coding/cli/strip_providers_binary_test.go::TestStripBedrockConverseStreamBuildOmitsAWSSDK,coding/cli/strip_upgrade_link_test.go::TestStripExtensionSDKGoBuildOmitsTheUpgradeAnalysis - Release note: A Piglet Binary that strips
bedrock-converse-streamlinks the AWS SDK again: the Bedrock retry backoff (ai/bedrock_retry_backoff.go) had nopig_strip_bedrock_converse_streambuild tag and importedaws-sdk-go-v2(27 packages). It carries the tag now, like the rest of the Bedrock provider.
BF-3ba74f2
fix(codingagent): wait for a read's rest on the route snapshot that found no modal
- Source: PiG's own defect
- Symptom: fix(codingagent): wait for a read's rest on the route snapshot that found no modal
- Root cause: dispatchTerminalRead took its route-change signal from a second snapshot after dispatchRoutedInput's modal check, and before any modal had armed the signal did not exist, so a modal arming off the loop left the main loop waiting for input that went to the modal. The wait now uses the signal of the snapshot that found no modal, and the signal exists before the first modal arms.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
3ba74f2827(2026-10-13) - Tests: None recorded.
BF-3cb7131
fix(sdk-py): stage pig_sdk.tui with the package, which pig_sdk imports at load; keep the pi-ai patterns…
- Source: PiG's own defect
- Symptom: fix(sdk-py): stage pig_sdk.tui with the package, which pig_sdk imports at load; keep the pi-ai patterns gofmt-clean
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
3cb7131f3a(2026-10-21) - Tests:
extensions/sdk-py/bundle_test.go::TestEveryPackageSourceIsBundled
BF-3ce6bcb
fix(changelog,source): /i scheme tests fold as JavaScript does, not as RE2's (?i)
- Source: PiG's own defect
- Symptom: fix(changelog,source): /i scheme tests fold as JavaScript does, not as RE2's (?i)
- Root cause: changelog.ts:14 URL_SCHEME_RE and git.ts:177 test a scheme with a non-unicode /i regex, which folds through toUpperCase and never maps U+017F (ſ) or U+212A (K) to s or k; RE2's (?i) does. A changelog link such as t was kept as an absolute URL where Pi rewrites it to the repository blob URL.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
3ce6bcb129(2026-10-13) - Tests:
coding/source/parse_git_oracle_test.go::TestParseGitMatchesPi,internal/codingagent/changelog_links_test.go::TestNormalizeChangelogLinksMatchesPi
BF-3cf03c0
A faux provider error message that carries a response id writes responseId after errorMessage in session…
- Source: PiG's own defect
- Symptom:
responseIdfollowedmodel. - Root cause: fauxAssistantMessage (faux.ts:97-99) writes errorMessage, then responseId, then timestamp after usage and stopReason. assistantErrorFirst put responseId in the head of the struct, before usage, so a faux error message that records a responseId persisted its keys in a different order than Pi's JSON.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
3cf03c047a(2026-10-10) - Tests:
ai/assistant_error_response_id_order_test.go::TestAssistantErrorFirstWritesResponseIDAfterErrorMessage - Release note: A faux provider error message that carries a response id writes
responseIdaftererrorMessagein session JSON, in the key order Pi'sfauxAssistantMessageuses. Before,responseIdfollowedmodel.
BF-3d12e48
fix(tui): tree selector cursor follows Pi's selectedIndex over an empty list
- Source: PiG's own defect
- Symptom: fix(tui): tree selector cursor follows Pi's selectedIndex over an empty list
- Root cause: Pi's up/down/page keys move selectedIndex without clamping, so over an empty filtered list it becomes -1 or 1 and applyFilter keeps it while lastSelectedId is null (tree-selector.ts handleInput, applyFilter). Go clamped to 0.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
3d12e48493(2026-10-10) - Tests:
internal/codingagent/tree_branching_oracle_test.go::TestTreeListBranchingRenderMatchesPiOracle
BF-3d475c4
Image resize strategy against live Pi: new TestResizeImageDimensionStrategyMatchesPi…
- Source: PiG's own defect
- Symptom: Image resize strategy against live Pi: new
TestResizeImageDimensionStrategyMatchesPi(internal/imageprocessing/resize_strategy_oracle_test.go;resizeImageInProcessthrough Photon vsResizeImage; 200 seeded PNGs 1-2,600 px, maxWidth/maxHeight 1-3,000, default/huge/1-byte maxBytes, unset/explicit mime). - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),6a6fffa638(2026-10-10),3d475c443d(2026-10-11) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResultand 20 more in docs/bug-ledger.json - Release note: Image resize strategy against live Pi: new
TestResizeImageDimensionStrategyMatchesPi(internal/imageprocessing/resize_strategy_oracle_test.go;resizeImageInProcessthrough Photon vsResizeImage; 200 seeded PNGs 1-2,600 px, maxWidth/maxHeight 1-3,000, default/huge/1-byte maxBytes, unset/explicit mime). One real fix, red first (15 of 300): a scaled side that rounds to zero (8000x1 under the 2000 limit gives a height of round(0.25) = 0) made PiG clamp it to one pixel and return an image, where Pi's Photon resize throws andresizeImageInProcessreturns null, so the image is dropped;ResizeImagenow returnsErrImageTooLarge(internal/imageprocessing/images.go). RegressionTestResizeImageReturnsNoImageWhenAScaledSideRoundsToZero. 5 mutations: 4 red; the 0.75 shrink factor survives because the shrink loop is reached only when every candidate misses maxBytes, which depends on the encoders (named as not compared). Not compared: the encoded bytes, which candidate wins a tight maxBytes, BMP input (PiG-only branch), the shrink steps.
BF-3def2a7
MCP servers whose names are numbers ("9", "42") load and report their errors in the order Pi does: a JSON…
- Source: PiG's own defect
- Symptom: MCP servers whose names are numbers (
"9","42") load and report their errors in the order Pi does: a JSON object lists its array-index keys first, in ascending order, then the others in file order. - Root cause: TestLoadMcpConfigMatchesPiOnRandomFiles loads 1500 random global and project mcp.json files through Pi's loadMcpConfig and PiG's LoadMcpConfig: 187 differed, among them every file with a numeric server name, because Object.entries lists array-index keys first and orderedjson.Object kept file order. Object.Set now inserts an array-index key at its ascending place before the other keys (Keys, MarshalJSON, Clone follow), with TestObjectEnumeratesArrayIndexKeysFirst. Dependent packages (codingagent, extension, mcpext, mcp, packagecontent) pass; the failures left in tools and the node-backed subprocess tests are the fd/rg and node-shim environment ones.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
10e9b5c109(2026-10-09),1f84136411(2026-10-09),5ceebfdae9(2026-10-09),b41d7817cc(2026-10-09),3def2a7bd8(2026-10-10) - Tests:
ai/anthropic_messages_response_oracle_test.go::TestAnthropicMessagesResponseMatchesPi,ai/models_store_bytes_oracle_test.go::TestModelsStoreFileBytesMatchPi,ai/models_store_lossless_test.go::TestModelsStoreKeepsUnknownFieldsThroughCloneAndFile,ai/openai_completions_response_oracle_test.go::TestOpenAICompletionsResponseMatchesPi,ai/openai_responses_response_oracle_test.go::TestOpenAIResponsesResponseMatchesPi,ai/transcript_replay_oracle_test.go::TestTranscriptReplayMatchesPi,coding/mcpext/config_load_oracle_test.go::TestLoadMcpConfigMatchesPiOnRandomFiles,coding/nested_recorder_oracle_test.go::TestNestedCallRecorderMatchesPiand 2 more in docs/bug-ledger.json - Release note: MCP servers whose names are numbers (
"9","42") load and report their errors in the order Pi does: a JSON object lists its array-index keys first, in ascending order, then the others in file order. The ordered JSON object the MCP configuration, tool arguments and other raw objects use now follows that rule.
BF-3e755ef
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: ai: SystemOneWireRequest keeps an explicit empty images list (system-one-shared.ts images?: string[]) instead of dropping it with omitempty, and the OpenRouter detection cites typesafe-system-one.ts:20. tools: WaitContext's hand-off goroutine moves out of its loop. gen-models: hasChatCompat compares with an encoder that does not escape <, > and &. Signed-off-by: Michael Kinsy 54590707+MichaelKinsy@users.noreply.github.com
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
3e755ef501(2026-10-22) - Tests:
ai/system_one_wire_test.go::TestSystemOneWireRequestImagesKeepAnExplicitEmptyList,ai/system_one_wire_test.go::TestTypesafeSystemOneDetectCompatShapes,internal/codingagent/tools/mutation_queue_test.go::TestFileMutationQueueWithPanickingCallbackStillDrains,internal/codingagent/tools/mutation_queue_test.go::TestMutationTicketWaitContextGaveUpTicketPassesOnAfterEveryPredecessor
BF-3ee123c
CI: the Linux / portmap job (make portmap-check) no longer runs out its 60-minute limit
- Source: PiG's own defect
- Symptom: CI: the
Linux / portmapjob (make portmap-check) no longer runs out its 60-minute limit. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: Linux
- Fixed in: 0.5.2
- Fix commits:
3ee123c6d5(2026-10-20, #255) - Tests:
agent/stream_fn_descriptor_test.go::TestAgentEffectiveStreamFunction,chord/host/manager_fork_test.go::TestForkInstanceReplaysControlMessagesReceivedBeforeTheFirstListener,chord/host/manager_ipc_listener_test.go::TestNodeChannelGivesTheFirstMessageListenerTheMessagesThatCameBeforeIt,chord/host/manager_ipc_listener_test.go::TestNodeChannelQueuesAMessageThatArrivesDuringTheReplayBehindTheBacklog,coding/cli/extension_stale_copy_test.go::TestStaleCopyDiagnosticFindsAnEditedCopyByItsModulePath,coding/cli/extension_upgrade_completeness_startup_test.go::TestStartupOffersEveryDriftedExtensionAndOneYesUpgradesThemAll,coding/cli/extension_upgrade_completeness_test.go::TestDryRunReportsWhatTheRewrittenTreeDoes,coding/cli/extension_upgrade_completeness_test.go::TestExtensionUpgradeCommandAppliesWhatItCanAndListsTheRestand 40 more in docs/bug-ledger.json - Release note: CI: the
Linux / portmapjob (make portmap-check) no longer runs out its 60-minute limit.portmapcheckbuilds each evidence package's test binary once instead of once per PORT_MAP row, runs rows that name the same tests of a package once, and keeps a marker row's killed-mutant verdict in its cached record. CI runs the check as three shards (make portmap-check-N,portmapcheck -shard N/3); every shard computes the same plan and judges its own rows, and the Linux verification job still requires all of them. Each row's requirement is unchanged: its named tests pass, they execute its mapped cited files, and a marker row's mutant makes them fail.
BF-3fa6e63
MCP servers with resources registered no tools until every resource was listed, so a slow listing (a large…
- Source: PiG's own defect
- Symptom: MCP servers with resources registered no tools until every resource was listed, so a slow listing (a large drive) kept their tools out of the first prompt.
- Root cause: Ports Pi packages/coding-agent/src/extensions/mcp/{runtime,cli,index}.ts: a connection no longer calls resources/list and resources/templates/list (fetchResources, refreshResources and the resources/list_changed handler are gone), so tools register without waiting for a slow listing. Connection keeps HasResources; /mcp shows "connected · N tools · resources" (index.ts describeState),
pig mcp listprints " offers resources" and --json reportsresources: trueinstead of the two counts (cli.ts list). The resource tools still list and read on demand. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
3fa6e63e8e(2026-10-12) - Tests:
coding/mcpext/cli_upstream_test.go::TestMcpCommandListReportsThatAServerOffersResourcesWithoutCountingThem,coding/mcpext/connection_test.go::TestMCPConnectionsConnectsToServersWithResourcesWithoutListingThem,coding/mcpext/responsive_test.go::TestMcpDescribeStateShowsThatAServerOffersResources - Release note: MCP servers with resources registered no tools until every resource was listed, so a slow listing (a large drive) kept their tools out of the first prompt. PiG no longer lists resources when connecting; the resource tools still list and read them on demand.
BF-3fbdfd2
A configured number in settings text prints as JavaScript's String(number): -0 is 0, and a magnitude of…
- Source: PiG's own defect
- Symptom: A configured number in settings text prints as JavaScript's
String(number):-0is0, and a magnitude of1e21or more, or below1e-6, gets an exponent (the fullscreen wheel scroll lines label and choices, and the invalid compaction token setting message). - Root cause: wheelScrollLinesLabel, wheelScrollLinesValues and compactionNumberText used strconv.FormatFloat(v, 'f', -1, 64): -0 printed -0 (JS: 0), 1e21 printed 22 digits (JS: 1e+21) and 1e-7 printed 0.0000001 (JS: 1e-7). Pi interpolates the configured number with String(), so they use jsnumber.String.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
3fbdfd2140(2026-10-10),6a6fffa638(2026-10-10),bc4cd62404(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,internal/codingagent/settings_number_text_test.go::TestSettingsNumberTextIsString,internal/jsstring/toprecision_oracle_test.go::TestToPrecisionAgainstNodeCorpusand 3 more in docs/bug-ledger.json - Release note: A configured number in settings text prints as JavaScript's
String(number):-0is0, and a magnitude of1e21or more, or below1e-6, gets an exponent (the fullscreen wheel scroll lines label and choices, and the invalid compaction token setting message).
BF-4027b34
A finished code-mode execution no longer keeps its closed VM reachable through its stopped halt-grace timer…
- Source: PiG's own defect
- Symptom: A finished code-mode execution no longer keeps its closed VM reachable through its stopped halt-grace timer, which stays in an idle scheduler's timer heap until that scheduler next runs.
- Root cause: The end RSS moved in 64 MiB steps (the bytes each script touches), which looked like the runtime returning pages late. It is not: wazero runs here without a custom allocator, so a VM's linear memory is one Go heap slice reserved at the heap limit plus 64 MiB of headroom (320 MiB), and the live Go heap after runtime.GC() was 0 to 6 of those above the start. A closed VM was still reachable.
- User impact: Behavior differed from Pi
- Platform: Linux
- Fixed in: 0.5.0
- Fix commits:
4027b34bc8(2026-10-13) - Tests:
codemode/resources_linux_test.go::TestConcurrentExecutionsReturnMemoryAndDescriptors - Release note: A finished code-mode execution no longer keeps its closed VM reachable through its stopped halt-grace timer, which stays in an idle scheduler's timer heap until that scheduler next runs. Each such VM held its whole linear memory (the heap limit plus 64 MiB, 320 MiB at a 256 MiB limit) until then. After 16 concurrent executions on a 196-CPU Linux host, the live Go heap after a collection was 0 to 6 of those memories above the start before the fix (up to 1.9 GiB) and under 0.1 MiB after it.
BF-4058f34
The /fork message picker checks its keys in Pi's order (up, down, confirm, cancel) when bindings overlap
- Source: PiG's own defect
- Symptom: The /fork message picker checks its keys in Pi's order (up, down, confirm, cancel) when bindings overlap.
- Root cause: The picker now uses NewInput with OnSubmit = selectHighlighted, shared with the confirm case.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
082355f166(2026-10-07),4058f34548(2026-10-07),56a0400b9c(2026-10-07),a7be906e71(2026-10-07),f2a2b3409b(2026-10-07) - Tests:
internal/codingagent/extension_dialog_remapping_test.go::TestExtensionDialogRemappingThroughOwnerLoop,internal/codingagent/remote_overlay_production_review_test.go::TestRemoteOverlayProductionRejectsCachedTerminalWidth,internal/codingagent/session_selector_oracle_test.go::TestSessionSelectorInputMatchesPi,internal/codingagent/tree_row_format_test.go::TestFormatTreeRow_AllEntryTypes,internal/codingagent/tree_selector_oracle_test.go::TestTreeSelectorInputMatchesPi,internal/codingagent/trust_selector_oracle_test.go::TestTrustSelectorInputMatchesPi,internal/codingagent/w3_pi_test.go::TestW3SessionPiComparison,tui/editor_bindings_oracle_test.go::TestEditorRebindingsMatchPiand 7 more in docs/bug-ledger.json - Release note: The /fork message picker checks its keys in Pi's order (up, down, confirm, cancel) when bindings overlap.
BF-40b5a89
Assistant messages in session files and RPC output list responseId before responseModel, Codex's…
- Source: PiG's own defect
- Symptom: Assistant messages in session files and RPC output list
responseIdbeforeresponseModel, Codex'sendTurnbeforerawStopReason, andrawStopReasonbeforeerrorMessage, the order in which Pi's providers create them. - Root cause: An earlier change says it writes "the static creation order every Pi provider produces", but it puts endTurn after rawStopReason and errorMessage. Codex is the one Pi provider that sets endTurn. It sets it on the terminal event inside mapCodexEvents (openai-codex-responses.ts:774-778), before it yields that event to processResponsesStream, whose finalizeResponse then creates rawStopReason and errorMessage (openai-responses-shared.ts:589-593). PiG wrote responseId,rawStopReason,endTurn.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
40b5a892b8(2026-10-10),c10cde302d(2026-10-10),f29e906cb3(2026-10-10) - Tests:
agent/assistant_member_order_codex_oracle_test.go::TestCodexAssistantEndTurnPrecedesRawStopReason,agent/assistant_member_order_oracle_test.go::TestAssistantLateMembersFollowPiCreationOrder,ai/openai_request_key_order_oracle_test.go::TestOpenAICompletionsRequestKeysFollowPiAssignmentOrder,ai/openai_responses_request_key_order_oracle_test.go::TestOpenAIResponsesRequestKeysFollowPiAssignmentOrder,coding/cli/rpc_assistant_member_order_test.go::TestRPCAssistantMessageMembersMatchTheSessionLineOrder,coding/cli/rpc_model_type_binary_test.go::TestRPCModelObjectsCarryTheRegistryModelType - Release note: Assistant messages in session files and RPC output list
responseIdbeforeresponseModel, Codex'sendTurnbeforerawStopReason, andrawStopReasonbeforeerrorMessage, the order in which Pi's providers create them.
BF-40bc651
The Anthropic event reader decides message_start and message_stop from the parsed event type as Pi does (not…
- Source: PiG's own defect
- Symptom: The Anthropic event reader decides message_start and message_stop from the parsed event type as Pi does (not the SSE event name), and a JSON null event fails with the TypeError Pi reports (a CRLF split across chunks still waits for the LF, D121).
- Root cause: Differential: 6000 seeded response bodies through Pi's iterateAnthropicEvents (the installed pi-ai dist, private generator exported by a load hook) and Pig's event reader, split at arbitrary bytes (never inside a CRLF: D121 keeps Pig's wait for the LF); events and the way each stream ends compared. 222 of 6000 differed.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
40bc651bae(2026-10-10) - Tests:
ai/anthropic_sse_oracle_test.go::TestAnthropicEventReaderMatchesPi - Release note: The Anthropic event reader decides message_start and message_stop from the parsed event type as Pi does (not the SSE event name), and a JSON null event fails with the TypeError Pi reports (a CRLF split across chunks still waits for the LF, D121).
BF-410d733
settings.json, trust.json and auth.json are written as Pi's JSON.stringify writes them: <, > and…
- Source: PiG's own defect
- Symptom:
settings.json,trust.jsonandauth.jsonare written as Pi'sJSON.stringifywrites them:<,>and&stay literal instead of\u0026-style escapes, so a command-backed key or ashellCommandPrefixsuch assource x &&keeps its text. - Root cause: Pi writes JSON.stringify(value, null, 2) for all three (settings-manager.ts:731, trust-manager.ts:126-135, auth-storage.ts:358,467,479). encoding/json and its SDK fork escape <, > and &, so
source x &&and!cmd && xwere persisted as \u0026, and settings.json kept the file's raw number and string forms. trust.json sorted keys by UTF-8 bytes where Array.prototype.sort compares UTF-16 units. Each file now goes through jsonstringify.Canonicalize and json.Indent. - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
410d7332a8(2026-10-06),8d23cfcee3(2026-10-06),a223fe3ff9(2026-10-06),fbd3640da9(2026-10-06) - Tests:
ai/auth_credential_payload_test.go::TestCredentialCustomJSONRetainsUTF16Payload,ai/auth_stringify_test.go::TestAuthStorageWritesJSONStringifyText,automation/ci/portlint/checks/checks_test.go::TestAllowMarkerNeedsAReason,automation/ci/portlint/checks/checks_test.go::TestAnalyzers,automation/ci/portlint/checks/checks_test.go::TestEveryCheckIsDocumented,automation/ci/portlint/checks/testdata/src/rawsymlink/a_test.go::TestBad,automation/ci/portlint/checks/testdata/src/rawsymlink/b_unix_test.go::TestUnixOnly,automation/ci/portlint/checks/testdata/src/testisolation/a_test.go::TestBadand 14 more in docs/bug-ledger.json - Release note:
settings.json,trust.jsonandauth.jsonare written as Pi'sJSON.stringifywrites them:<,>and&stay literal instead of\u0026-style escapes, so a command-backed key or ashellCommandPrefixsuch assource x &&keeps its text. Saving a setting also rewrites numbers and strings in Pi's form, andtrust.jsonsorts its paths by UTF-16 code units as Pi does.
BF-411a8cb
Background runs of pi-subagents (and other helpers an extension starts as a detached Node process) complete…
- Source: PiG's own defect
- Symptom: Background runs of pi-subagents (and other helpers an extension starts as a detached Node process) complete under PiG.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
411a8cb698(2026-10-12) - Tests:
test/docs-drift/commands_test.go::TestDocumentedPigCommandsAreValid - Release note: Background runs of pi-subagents (and other helpers an extension starts as a detached Node process) complete under PiG. The harness package now carries the host peer packages Pi's pi-coding-agent depends on, and a process with no extension runtime streams model requests through a bridge process it owns (the internal provider-bridge command), D74.
BF-4163646
AuthStorage file semantics: new live-Pi oracle TestAuthStorageFileMatchesPi…
- Source: PiG's own defect
- Symptom:
AuthStoragefile semantics: new live-Pi oracleTestAuthStorageFileMatchesPi(ai/auth_storage_file_oracle_test.go; 600 auth.json files x 1-5 operations over read, list, modify and delete: absent, empty, corrupt, byte-order-marked and non-object files,$VAR/${VAR}/!command/credential-env key resolution, OAuth and unknown types, integer-index provider order). - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),e8cc072521(2026-10-08),433d232815(2026-10-09),41636465fe(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/auth_storage_file_oracle_test.go::TestAuthStorageFileMatchesPiand 11 more in docs/bug-ledger.json - Release note:
AuthStoragefile semantics: new live-Pi oracleTestAuthStorageFileMatchesPi(ai/auth_storage_file_oracle_test.go; 600 auth.json files x 1-5 operations over read, list, modify and delete: absent, empty, corrupt, byte-order-marked and non-object files,$VAR/${VAR}/!command/credential-env key resolution, OAuth and unknown types, integer-index provider order). Real fixes for a file whose JSON is not an object (Pi keeps whateverJSON.parsereturns):nullmade PiG treat the file as empty where Pi's read, list, modify and delete all throw (Cannot read properties of null (reading 'x')/Cannot convert undefined or null to object); an array was rejected where Pi reads and lists it as an index-keyed object, turns it into an object on modify and rewrites it as an array (deleted elementnull) on delete; a number or boolean was rejected where Pi reads it as empty and a delete rewrites it as itself; anullcredential entry madelistreturn an entry where Pi'scredential.typethrows. 10 mutations red; one equivalent (the root shape of the post-write read cache is never rewritten). Not compared: V8's JSON parse error text, string-valued files (JavaScript indexes their characters), file modes, cross-process locking.
BF-41b305d
Codemode rejects a // @options: line that is not the first line of the script with an error before the…
- Source: PiG's own defect
- Symptom: The line used to be ignored, so its
timeout_msnever applied and the script ran without a deadline (Pi #10631). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
41b305dc69(2026-10-12) - Tests:
codemode/source_test.go::TestParseCodemodeSourceOnlyTreatsTheFirstLineAsAnOptionsLine,codemode/source_test.go::TestParseCodemodeSourceRejectsAnOptionsLineThatIsNotTheFirstLine,coding/codemode_session_upstream_test.go::TestUpstreamCodemodeOptionsAndStore,durable/codemode/call_test.go::TestACallLaunchesOnlyOnceItsIssuedCountIsCommitted,durable/codemode/call_test.go::TestAScriptsTimeoutLimitsItsRun,durable/codemode/call_test.go::TestCutCountsUTF16Units,durable/codemode/call_test.go::TestSameJSONIgnoresKeyOrder,durable/codemode/call_test.go::TestTheFirstRunRecordsTheVMsFingerprintand 1 more in docs/bug-ledger.json - Release note: Codemode rejects a
// @options:line that is not the first line of the script with an error before the script runs. The line used to be ignored, so itstimeout_msnever applied and the script ran without a deadline (Pi #10631).
BF-42116f8
Durable: the system baseline written after a compaction now leads the model context when the kept history…
- Source: PiG's own defect
- Symptom: Durable: the system baseline written after a compaction now leads the model context when the kept history includes an assistant message, as does a first baseline written after earlier answers, instead of following the new input; Anthropic and OpenAI Codex requests no longer lose the initial prompt and tool set.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
42116f895b(2026-10-14) - Tests:
durable/harness/context_pi_oracle_test.go::TestContextDerivationMatchesPi,durable/harness/harness_compaction_test.go::TestManualCompaction,durable/harness/harness_compaction_test.go::TestOverflowCompaction,durable/harness/harness_context_test.go::TestConversationContext - Release note: Durable: the system baseline written after a compaction now leads the model context when the kept history includes an assistant message, as does a first baseline written after earlier answers, instead of following the new input; Anthropic and OpenAI Codex requests no longer lose the initial prompt and tool set. Other system messages after an assistant message stay in place. Carried from Pi 5713c950 (earendil-works/pi#10904)
BF-4259631
An extension's direct call into a pi-ai API module (openAICompletionsApi() and the other bridged APIs)…
- Source: PiG's own defect
- Symptom: An extension's direct call into a pi-ai API module (
openAICompletionsApi()and the other bridged APIs) sends the model's base URL as given, as Pi's API modules do. - Root cause:
- ai.WrapBuiltinProvider built a fresh provider (new http.Client and Transport) for every request of a Cloudflare or OpenCode per-model provider, so keep-alive connections were never reused and the provider's configured client was dropped. withEndpoint now keeps the provider's client and returns the provider itself when the endpoint is unchanged. Pi's cloudflareStreams (cloudflare-stream.ts:21-28) swaps only the model. TestWrapBuiltinProviderSendsThroughTheProvidersClient fails on the old code (requests left the injected client). - An extension's call into a bridged pi-ai API module (apiLeaf) was wrapped too, so the host materialized Cloudflare placeholders that Pi's API modules send as…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
24c157ae1d(2026-10-09),42596315cb(2026-10-09),7a0908ebbf(2026-10-09) - Tests:
ai/cloudflare_test.go::TestCloudflareStreamsResolveTheModelForBothEntryPoints,ai/cloudflare_test.go::TestOnlyTheCloudflareProvidersWrapTheirStreams,ai/provider_env_test.go::TestResolveCloudflareModel_UsesProviderEnv,ai/provider_wrap_test.go::TestWrapBuiltinProviderSendsThroughTheProvidersClient,coding/model_api_request_test.go::TestIndependentAPIRequestSendsTheModelEndpointAsGiven,durable/document_semantics_test.go::TestConversationSemanticsFixScopeAndHistory,test/parity/interface-closure/autobind/error_rationale_test.go::TestErrorRuntimeRationaleCitesC5OnlyForModelsError - Release note: An extension's direct call into a pi-ai API module (
openAICompletionsApi()and the other bridged APIs) sends the model's base URL as given, as Pi's API modules do. Only the Cloudflare providers' stream wrapper materializes{CLOUDFLARE_ACCOUNT_ID}and{CLOUDFLARE_GATEWAY_ID}.
BF-42c059a
fix(tui): bash block strips ANSI with Pi's utils/ansi.ts rule
- Source: PiG's own defect
- Symptom: fix(tui): bash block strips ANSI with Pi's utils/ansi.ts rule
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
42c059a149(2026-10-10) - Tests:
internal/codingagent/bash_execution_oracle_test.go::TestBashExecutionComponentMatchesPi
BF-42e3d0e
fix(hljs): a class that negates only \r still reaches across lines; gate the streamed code block Linear
- Source: PiG's own defect
- Symptom: fix(hljs): a class that negates only \r still reaches across lines; gate the streamed code block Linear
- Root cause: The previous commit carries only the changelog fragment rename (a failed git add left these files out). - reach.class: a negated class counted as excluding a line break when it named \r, U+2028 or U+2029, but \r* matches "\n" without bound (JS RegExp). Only "\n" (literal, escape or \s) makes a negated class line-free now. No bundled grammar has such a class today, so resume points are unchanged. - TestScalingStreamedCodeBlockHighlight: python block streamed 24 bytes per frame, 2k -> 20k bytes: allocs slope 1.09, bytes 1.45 (bound Linear, bytes linearWithFrameCopy).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
42e3d0eb5b(2026-10-10) - Tests:
tui/highlight_resume_scaling_norace_test.go::TestScalingStreamedCodeBlockHighlight,tui/internal/hljs/resume_test.go::TestPatternReach
BF-4312595
fix(ai,durable): keep a value that is not JSON an error when a message or a nested result is encoded
- Source: PiG's own defect
- Symptom: fix(ai,durable): keep a value that is not JSON an error when a message or a nested result is encoded
- Root cause: Fix-Commit:
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
43125951f9(2026-10-24) - Tests: None recorded.
BF-432ab7f
An agent assistant message that holds an empty diagnostics array writes it as [], as Pi does, instead of…
- Source: PiG's own defect
- Symptom: An agent assistant message that holds an empty
diagnosticsarray writes it as[], as Pi does, instead of leaving the member out. - Root cause: JSON.stringify writes a present diagnostics member even when it is empty (ai types.ts:577 diagnostics?: AssistantMessageDiagnostic[]); omitempty dropped it from both assistant wire shapes. omitzero leaves out only an absent (nil) array.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
432ab7fd80(2026-10-13) - Tests:
agent/message_json_member_order_test.go::TestAssistantMessageJSONKeepsAnEmptyDiagnosticsArray - Release note: An agent assistant message that holds an empty
diagnosticsarray writes it as[], as Pi does, instead of leaving the member out.
BF-433d232
A request sent after /tree navigation to a point between parallel tool calls and their results no longer…
- Source: PiG's own defect
- Symptom: A request sent after
/treenavigation to a point between parallel tool calls and their results no longer fails with "unexpected tool_use_id in tool_result blocks". - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunkand 1 more in docs/bug-ledger.json - Release note: A request sent after
/treenavigation to a point between parallel tool calls and their results no longer fails with "unexpected tool_use_id in tool_result blocks". A branch summary, compaction summary, bash execution or custom message now ends the pending tool calls like any user turn, so the placeholder results come before it. A system message between a tool call and its results is now held until after the results, as in Pi.
BF-433d232-2
An extension custom message whose content is a block array (text or images), or an empty string, now reaches…
- Source: PiG's own defect
- Symptom: An extension custom message whose content is a block array (text or images), or an empty string, now reaches the model as a user message, as in Pi; it was dropped before.
- Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunkand 1 more in docs/bug-ledger.json - Release note: An extension custom message whose content is a block array (text or images), or an empty string, now reaches the model as a user message, as in Pi; it was dropped before. Branch summaries, compaction summaries, bash executions and custom messages are now sent as text-block arrays, so Anthropic Messages and OpenAI Completions requests carry the same content shape as Pi. Any of these messages ends pending tool calls like a user turn.
BF-433d232-3
DefaultResourceLoader.reload: new live-Pi oracle TestDefaultResourceLoaderReloadMatchesPi…
- Source: PiG's own defect
- Symptom:
DefaultResourceLoader.reload: new live-Pi oracleTestDefaultResourceLoaderReloadMatchesPi(coding/resource_loader_reload_oracle_test.go; 400 random trees of user, project, ancestor.agents, home and settings-listed prompt templates, skills, themes, SYSTEM.md, APPEND_SYSTEM.md and AGENTS.md; trusted and untrusted; no-skills/prompts/themes/context switches; systemPrompt, appendSystemPrompt and additional-path options incl. missing and home-relative paths). - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e8cc072521(2026-10-08),433d232815(2026-10-09),da99854d1b(2026-10-09) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPiand 6 more in docs/bug-ledger.json - Release note:
DefaultResourceLoader.reload: new live-Pi oracleTestDefaultResourceLoaderReloadMatchesPi(coding/resource_loader_reload_oracle_test.go; 400 random trees of user, project, ancestor.agents, home and settings-listed prompt templates, skills, themes, SYSTEM.md, APPEND_SYSTEM.md and AGENTS.md; trusted and untrusted; no-skills/prompts/themes/context switches; systemPrompt, appendSystemPrompt and additional-path options incl. missing and home-relative paths). Compares prompts, skills and themes (names, descriptions, contents, paths, source info), collision/validation diagnostics (including Pi's installed theme JSON validator messages), system and appended prompts with sources, and context files. No bug found. 12 mutations red (discovery trust, dedupe winner/loser, message, append/source handling, no-prompts/skills/themes/context switches, mergePaths dedupe, global discovery, missing-path message). Equivalent mutants: the|| len(paths) > 0prompt/theme gates (loading zero paths yields nothing),CanonicalizePathin mergePaths (corpus has no symlinks or case-folded duplicates), the missing-path diagnostic skip and the DefaultSourceInfoForPath fallback (every corpus path carries resolved metadata). Not compared: extensions, Packages (npm/git), symlinked duplicates.
BF-433d232-4
ReadOnlyAuthStorage: new live-Pi oracle TestReadOnlyAuthStorageMatchesPi…
- Source: PiG's own defect
- Symptom:
ReadOnlyAuthStorage: new live-Pi oracleTestReadOnlyAuthStorageMatchesPi(ai/auth_storage_readonly_oracle_test.go; 800 auth.json files x 1-5 operations over read, list, refused modify and delete, and a file rewrite between operations that a loaded store must not see; valid and invalid credential shapes: non-string key, non-string env value, env null/array, OAuth missing access/refresh/expires or with a string expiry, null/array/scalar entries and roots). - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),e8cc072521(2026-10-08),433d232815(2026-10-09),da99854d1b(2026-10-09) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPiand 8 more in docs/bug-ledger.json - Release note:
ReadOnlyAuthStorage: new live-Pi oracleTestReadOnlyAuthStorageMatchesPi(ai/auth_storage_readonly_oracle_test.go; 800 auth.json files x 1-5 operations over read, list, refused modify and delete, and a file rewrite between operations that a loaded store must not see; valid and invalid credential shapes: non-string key, non-string env value, env null/array, OAuth missing access/refresh/expires or with a string expiry, null/array/scalar entries and roots). Results and thrown messages match; no bug found. 9 mutations red (key/env/OAuth validation, command-key bypass, refused modify, null root, error text). Equivalent mutants: the finite-expiry check (Go's JSON decoder already rejects1e999), the empty-key and API-key type guards in Read (resolveStoredCredentialrepeats both). Not compared: V8 text after "Failed to read auth.json: ".
BF-43a2293
Key order of non-index keys stays byte order (a Go map has no insertion order): question Q-state-order filed
- Source: PiG's own defect
- Symptom: Key order of non-index keys stays byte order (a Go map has no insertion order): question Q-state-order filed
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
43a22934f1(2026-10-11) - Tests:
ai/llama_cpp_render_oracle_test.go::TestLlamaCppRenderDocsDump,ai/llama_cpp_render_oracle_test.go::TestLlamaCppRenderDocsParity,ai/llama_cpp_render_oracle_test.go::TestLlamaCppRenderQuestionMatchesPi,internal/jsstring/stringify_test.go::TestMarshalJSONOrdersArrayIndexKeysLikeJSONStringify,test/parity/scenarios/providers-registry/07-llama-classify-render-question.toml
BF-43c46f5
Chord host timers (reload vote, gate and retire deadlines, link pings, service linger, host manager grace…
- Source: PiG's own defect
- Symptom: A deadline above the limit used to never fire and a zero one fired at once.
- Root cause: Pi's chord timers are Node timers: lib/internal/timers.js Timeout runs a delay outside [1, TIMEOUT_MAX = 2^31-1] ms after 1 ms, and insert truncates the rest to whole milliseconds. The Go port handed the raw Duration to time.AfterFunc/NewTimer/NewTicker, so a deadline above TIMEOUT_MAX never fired, a zero or negative one fired at once, and fractions were kept.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
43c46f5394(2026-10-13) - Tests:
chord/host/node_delay_test.go::TestAwaitVoteDeadlineAboveNodeTimeoutMaxRejectsAfterOneMillisecond,chord/host/node_delay_test.go::TestNodeDelayMatchesNodeTimers - Release note: Chord host timers (reload vote, gate and retire deadlines, link pings, service linger, host manager grace, ready timeout and keepAlive interval) wait as Node's
setTimeoutdoes: a delay above 2147483647 ms, below 1 ms or zero runs after 1 ms, and fractional milliseconds are truncated. A deadline above the limit used to never fire and a zero one fired at once.
BF-43d2597
@ file autocomplete finds paths for queries that hold regex metacharacters such as [ or (, for a query…
- Source: A fix Pi shipped that PiG ported
- Symptom:
@file autocomplete finds paths for queries that hold regex metacharacters such as[or(, for a query with or without a/(Pi 612a172e, #10972). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
43d259770e(2026-10-21) - Tests:
agent/tool_replay_error_test.go::TestToolReplayErrorMatchesPi,coding/cli/rpc_replacement_order_test.go::TestRPCSwitchSessionFailsWithTheSystemReplayTypeError,coding/runtime_switch_wrong_typed_system_test.go::TestSwitchSessionRejectsAWrongTypedSystemReplayLikePi,coding/session_resume_default_tools_port_test.go::TestResumeRestoresTheRecordedLoadoutPort,internal/codingagent/compaction/context_edit_test.go::TestCompactAppliesCustomInstructionsToTheTurnPrefixSummaryOfASplitTurn,test/extension-conformance/resume_tools_test.go::TestResumeActivatesExtensionToolsTheBranchNeverMentionedAcrossSDKs,tui/autocomplete_upstream_test.go::TestUpstreamAutocompleteFD,tui/file_autocomplete_test.go::TestBuildFdPathQuery - Release note:
@file autocomplete finds paths for queries that hold regex metacharacters such as[or(, for a query with or without a/(Pi 612a172e, #10972).
BF-43d2597-2
/compact <instructions> applies the instructions to the summary of the start of a turn that compaction…
- Source: A fix Pi shipped that PiG ported
- Symptom:
/compact <instructions>applies the instructions to the summary of the start of a turn that compaction splits, and no longer ignores them when the split turn is the only new content since the last compaction (Pi adfc1446, #11058). - Root cause:
/compact <instructions>applies the instructions to the summary of the start of a turn that compaction splits, and no longer ignores them when the split turn is the only new content since the last compaction (Pi adfc1446, #11058). - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
43d259770e(2026-10-21) - Tests:
agent/tool_replay_error_test.go::TestToolReplayErrorMatchesPi,coding/cli/rpc_replacement_order_test.go::TestRPCSwitchSessionFailsWithTheSystemReplayTypeError,coding/runtime_switch_wrong_typed_system_test.go::TestSwitchSessionRejectsAWrongTypedSystemReplayLikePi,coding/session_resume_default_tools_port_test.go::TestResumeRestoresTheRecordedLoadoutPort,internal/codingagent/compaction/context_edit_test.go::TestCompactAppliesCustomInstructionsToTheTurnPrefixSummaryOfASplitTurn,test/extension-conformance/resume_tools_test.go::TestResumeActivatesExtensionToolsTheBranchNeverMentionedAcrossSDKs,tui/autocomplete_upstream_test.go::TestUpstreamAutocompleteFD,tui/file_autocomplete_test.go::TestBuildFdPathQuery - Release note:
/compact <instructions>applies the instructions to the summary of the start of a turn that compaction splits, and no longer ignores them when the split turn is the only new content since the last compaction (Pi adfc1446, #11058).
BF-43d2597-3
A resumed session and /tree navigation activate the extension tools the branch never added or removed, such…
- Source: A fix Pi shipped that PiG ported
- Symptom: A resumed session and
/treenavigation activate the extension tools the branch never added or removed, such as tools of an extension installed after the session started, unless they are registered withdefaultActive: false; a tool the branch removed, also before a compaction, stays inactive. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
43d259770e(2026-10-21) - Tests:
agent/tool_replay_error_test.go::TestToolReplayErrorMatchesPi,coding/cli/rpc_replacement_order_test.go::TestRPCSwitchSessionFailsWithTheSystemReplayTypeError,coding/runtime_switch_wrong_typed_system_test.go::TestSwitchSessionRejectsAWrongTypedSystemReplayLikePi,coding/session_resume_default_tools_port_test.go::TestResumeRestoresTheRecordedLoadoutPort,internal/codingagent/compaction/context_edit_test.go::TestCompactAppliesCustomInstructionsToTheTurnPrefixSummaryOfASplitTurn,test/extension-conformance/resume_tools_test.go::TestResumeActivatesExtensionToolsTheBranchNeverMentionedAcrossSDKs,tui/autocomplete_upstream_test.go::TestUpstreamAutocompleteFD,tui/file_autocomplete_test.go::TestBuildFdPathQuery - Release note: A resumed session and
/treenavigation activate the extension tools the branch never added or removed, such as tools of an extension installed after the session started, unless they are registered withdefaultActive: false; a tool the branch removed, also before a compaction, stays inactive. Built-in tools keep followingdefaultTools(Pi bd3c9b82, #11062, #11075).
BF-43d2597-4
Switching to a session whose system message holds a toolsAdded or toolsRemoved member the loadout restore…
- Source: A fix Pi shipped that PiG ported
- Symptom: Switching to a session whose system message holds a
toolsAddedortoolsRemovedmember the loadout restore cannot read fails with the TypeError of Pi's tool-list replay; a system message with an unreadablecontentno longer fails the switch (Pi bd3c9b82). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
43d259770e(2026-10-21) - Tests:
agent/tool_replay_error_test.go::TestToolReplayErrorMatchesPi,coding/cli/rpc_replacement_order_test.go::TestRPCSwitchSessionFailsWithTheSystemReplayTypeError,coding/runtime_switch_wrong_typed_system_test.go::TestSwitchSessionRejectsAWrongTypedSystemReplayLikePi,coding/session_resume_default_tools_port_test.go::TestResumeRestoresTheRecordedLoadoutPort,internal/codingagent/compaction/context_edit_test.go::TestCompactAppliesCustomInstructionsToTheTurnPrefixSummaryOfASplitTurn,test/extension-conformance/resume_tools_test.go::TestResumeActivatesExtensionToolsTheBranchNeverMentionedAcrossSDKs,tui/autocomplete_upstream_test.go::TestUpstreamAutocompleteFD,tui/file_autocomplete_test.go::TestBuildFdPathQuery - Release note: Switching to a session whose system message holds a
toolsAddedortoolsRemovedmember the loadout restore cannot read fails with the TypeError of Pi's tool-list replay; a system message with an unreadablecontentno longer fails the switch (Pi bd3c9b82).
BF-448d2a9
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: SendMessage returns at once and the Session is busy from the call (Pi PR #10739 _startRun); pending nextTurn messages ride with a custom run; plan-mode example skips non-user runs; conformance row TestBeforeAgentStartTriggerAcrossSDKs and TS augmentation; D140 reverted (models.md:66 intends replacement; test pins it); UTF-16 tail test for D141.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
448d2a9568(2026-10-09) - Tests:
coding/session_pi_bugs_test.go::TestIdleCustomMessageSendReturnsAtOnceAndTheStartingRunIsBusy,examples/extensions/plan-mode/before_agent_start_test.go::TestPlanContextIsInjectedOnlyBeforeUserRuns,internal/codingagent/models_json_replace_test.go::TestModelsJSONEntryForAnExposedModelReplacesItAndModelOverridesKeepItsFields,test/extension-conformance/before_agent_start_trigger_test.go::TestBeforeAgentStartTriggerAcrossSDKs
BF-448ff71
A panic in an extension error listener is now named on stderr instead of vanishing, and extension SDK…
- Source: PiG's own defect
- Symptom: A panic in an extension error listener is now named on stderr instead of vanishing, and extension SDK terminal-input handlers and overlay invalidators that panic are reported in the extension log.
- Root cause: QUALITY-AUDIT-050 5.3: 23 production recover() sites discarded the panic value, only one of them inside the packages bare-recover scanned.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
448ff71f1f(2026-10-09) - Tests:
automation/ci/divguard/divguard_test.go::TestBareRecoverScansEveryProductionPackage,coding/mcpext/notify_panic_internal_test.go::TestNotifyPanicPropagatesExceptOnTheLoadFailurePath,coding/session_event_panic_test.go::TestReportExtensionErrorNamesAPanickingListener,extensions/sdk/recovered_panic_reporting_test.go::TestInvalidatorPanicOnStopIsReported,extensions/sdk/recovered_panic_reporting_test.go::TestSameOverlayComponentNeverPanicsOnUncomparableValues,extensions/sdk/recovered_panic_reporting_test.go::TestTerminalInputHandlerPanicIsReportedAndYieldsNoVerdict - Release note: A panic in an extension error listener is now named on stderr instead of vanishing, and extension SDK terminal-input handlers and overlay invalidators that panic are reported in the extension log.
BF-448ff71-2
MCP notifications no longer swallow every panic from the notify callback; only the "MCP failed to load"…
- Source: PiG's own defect
- Symptom: MCP notifications no longer swallow every panic from the notify callback; only the "MCP failed to load" message ignores a throwing notify, as Pi does.
- Root cause: QUALITY-AUDIT-050 5.3: 23 production recover() sites discarded the panic value, only one of them inside the packages bare-recover scanned.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
448ff71f1f(2026-10-09) - Tests:
automation/ci/divguard/divguard_test.go::TestBareRecoverScansEveryProductionPackage,coding/mcpext/notify_panic_internal_test.go::TestNotifyPanicPropagatesExceptOnTheLoadFailurePath,coding/session_event_panic_test.go::TestReportExtensionErrorNamesAPanickingListener,extensions/sdk/recovered_panic_reporting_test.go::TestInvalidatorPanicOnStopIsReported,extensions/sdk/recovered_panic_reporting_test.go::TestSameOverlayComponentNeverPanicsOnUncomparableValues,extensions/sdk/recovered_panic_reporting_test.go::TestTerminalInputHandlerPanicIsReportedAndYieldsNoVerdict - Release note: MCP notifications no longer swallow every panic from the notify callback; only the "MCP failed to load" message ignores a throwing notify, as Pi does.
BF-44f8d86
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: The Node adoption change edits runtime.mjs, so the embedded archive and its digest are regenerated (go generate ./coding/extension/host/subprocess).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
44f8d86c29(2026-10-20) - Tests: None recorded.
BF-4599fab
fix(ai): an iteration's shared cancellation does not compare contexts
- Source: PiG's own defect
- Symptom: fix(ai): an iteration's shared cancellation does not compare contexts
- Root cause: An earlier change's one-registration-per-iteration path compared the iterator's recorded context with the context of each later next() using . Go panics when it compares two interface values whose identical dynamic type is not comparable, so Events(ctx) with a context struct carrying a slice or map (passed by value) panicked at its second wait: "comparing uncomparable type". Both iteration loops (events() turn path, observeEvents) pass the same captured context to every next(), so the comparison never decided anything. The registration is now armed once, keyed on stop nil, and contexts are not compared.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
4599fab32c(2026-10-12) - Tests:
ai/event_stream_cancellation_test.go::TestIterationOnAnUncomparableContextWaitsForEachEvent,ai/event_stream_waiter_disarm_test.go::TestContinuationIteratorDisarmsDeliveredWaits
BF-45ec149
Read-only credential storage reports an unreadable or malformed auth.json as Pi does: `Failed to read…
- Source: PiG's own defect
- Symptom: Read-only credential storage reports an unreadable or malformed
auth.jsonas Pi does:Failed to read auth.json:followed by Node's errno text (for exampleEISDIR: illegal operation on a directory, read) or the JSON.parse SyntaxError text (for exampleUnexpected end of JSON input), instead of Go's error text. - Root cause: The writable store already validates with jsonparse and maps read errors with nodeerrno; the read-only load now does the same.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
45ec14925a(2026-10-10) - Tests:
ai/auth_storage_readonly_oracle_test.go::TestReadOnlyAuthStorageMatchesPi,ai/auth_storage_readonly_read_error_test.go::TestReadOnlyAuthStorageReportsReadAndParseErrorsWithNodeMessages - Release note: Read-only credential storage reports an unreadable or malformed
auth.jsonas Pi does:Failed to read auth.json:followed by Node's errno text (for exampleEISDIR: illegal operation on a directory, read) or the JSON.parse SyntaxError text (for exampleUnexpected end of JSON input), instead of Go's error text.
BF-4610c9f
A ! or !! command's block draws the bytes Pi's BashExecutionComponent draws: its rows are padded to the…
- Source: PiG's own defect
- Symptom: A
!or!!command's block draws the bytes Pi'sBashExecutionComponentdraws: its rows are padded to the terminal width, the exit status, cancelled and truncation lines use the theme's error and warning colors instead of fixed red and yellow, the collapse hint uses the theme's key hint with the configured expand key, the spinner names the configured cancel keys (escape/ctrl+cby default) instead ofEsc, no blank row follows the bottom border, the output Pi would cut for the model's context (the last 2000 lines or 50 KB) is cut the same way, and ANSI escapes and carriage returns in the output are cleaned as Pi cleans them. - Root cause: TestTaskAbort's abort-again case let the caller join before the run phase started, so under load the caller's cancel raced the task settling and the waiter returned nil instead of the cancel cause. Pi's scheduler joins only a run invocation already on the line (scheduler.ts:274-296); the test now waits for the run phase first.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a4753ec61e(2026-10-06, #170),ba9c37b247(2026-10-06, #171),165cf5884d(2026-10-07, #180),3499c2bea7(2026-10-07, #181),9b63b2bc4e(2026-10-07, #176),4610c9f4a1(2026-10-08, #189),50e81255de(2026-10-08, #174) - Tests:
coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleBusyEntriesAreSkippedNotErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledBeforeStartTouchesNothing,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledDuringTheLastEntryReportsTheCancellation,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleEntryLeasedAfterClassificationIsSkippedNotAnError,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleOtherErrorsStayErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleStopsWhenItsContextIsCancelled,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestIsBusyErrorLooksThroughWrappedJoinsand 57 more in docs/bug-ledger.json - Release note: A
!or!!command's block draws the bytes Pi'sBashExecutionComponentdraws: its rows are padded to the terminal width, the exit status, cancelled and truncation lines use the theme's error and warning colors instead of fixed red and yellow, the collapse hint uses the theme's key hint with the configured expand key, the spinner names the configured cancel keys (escape/ctrl+cby default) instead ofEsc, no blank row follows the bottom border, the output Pi would cut for the model's context (the last 2000 lines or 50 KB) is cut the same way, and ANSI escapes and carriage returns in the output are cleaned as Pi cleans them.
BF-4649166
fix(sdk): place the arrival-order start in the request scope so a tool_call round trip keeps its 68…
- Source: PiG's own defect
- Symptom: fix(sdk): place the arrival-order start in the request scope so a tool_call round trip keeps its 68 allocations; test the emptydrop claims; keep an empty operations list
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
46491664b6(2026-10-21) - Tests:
coding/create_agent_session_test.go::TestResolveSDKToolSelectionEmptyExcludeToolsIsAbsent,coding/extension/host/subprocess/host_builtin_tool_test.go::TestBuiltinToolDefinitionsHaveNoEmptyPromptGuidelines,coding/extension/host/subprocess/host_builtin_tool_test.go::TestBuiltinToolOptionsKeepAnEmptyOperationsList
BF-46abed3
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
46abed3453(2026-10-21) - Tests: None recorded.
BF-471c46e
An MCP tool or resource link with an explicit empty title keeps it, as Pi does, instead of falling back to…
- Source: PiG's own defect
- Symptom: An MCP tool or resource link with an explicit empty
titlekeeps it, as Pi does, instead of falling back to the annotation title or the name. - Root cause: tools.ts: block.title ?? block.name and tool.title ?? tool.annotations?.title replace only a missing title; mcp.Tool and mcp.ContentBlock track presence (HasTitle). index.ts: hideTools and the re-registration of dropped tools iterate a Set in insertion order; serverTools is an insertion-ordered set (the dropped-tool order was a map iteration).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
471c46e063(2026-10-05) - Tests:
coding/mcpext/extension_test.go::TestAgentSessionMCPHidesToolsInTheOrderTheServerListedThem,coding/mcpext/tools_test.go::TestMCPToolsKeepAnExplicitEmptyTitle,mcp/client_cursor_test.go::TestToolsAndContentBlocksTrackWhetherTheServerSentATitle - Release note: An MCP tool or resource link with an explicit empty
titlekeeps it, as Pi does, instead of falling back to the annotation title or the name.
BF-4789fa5
thinking_level_select and turn_start handlers are covered through the production Session: a handler sees…
- Source: PiG's own defect
- Symptom:
thinking_level_selectandturn_starthandlers are covered through the production Session: a handler sees the clamped effective level only when it changes, andturn_startcarries the per-run turn index and the time of the turn. - Root cause: Pi's handler returns an object whose exec runs the user's command (an SSH extension's remote shell). Functions cannot cross the socket, so the reply names the object by handle and the host's BashOperations.Exec sends user_bash_exec to the owner connection: onData chunks stream as tool_update notifications before the exit code, the request's cancellation is the exec's signal, a rejection fails the command, and the extension drops the object when the host's is collected (bash_operations_release). Node, Go, Python and Rust SDKs; the embedded Node runtime is regenerated.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
4789fa51f0(2026-10-08) - Tests:
coding/extension/host/subprocess/runtime_node_user_bash_operations_test.go::TestNodeRuntimeUserBashOperations,coding/extension/host/subprocess/user_bash_operations_test.go::TestUserBashOperationsExecAbortsWithTheContext,coding/extension/host/subprocess/user_bash_operations_test.go::TestUserBashOperationsReleaseTheExtensionHandleWhenTheHostDropsThem,coding/extension/host/subprocess/user_bash_operations_test.go::TestUserBashOperationsReplyWithResultOrWithoutHandleIsLeftRaw,coding/session_extension_events_upstream_test.go::TestSessionEmitsThinkingLevelSelectOnlyForAChangeWithTheEffectiveLevel,coding/session_extension_events_upstream_test.go::TestSessionEmitsTurnStartWithThePerRunTurnIndexAndATimestamp,extensions/sdk-py/tests/test_user_bash_operations.py,extensions/sdk/user_bash_operations_test.go::TestUserBashOperationsStayInTheExtensionUntilTheHostReleasesThemand 4 more in docs/bug-ledger.json - Release note:
thinking_level_selectandturn_starthandlers are covered through the production Session: a handler sees the clamped effective level only when it changes, andturn_startcarries the per-run turn index and the time of the turn.
BF-47c3007
/mcp now opens at once while servers are still connecting, and connection actions (reconnect, enable…
- Source: A fix Pi shipped that PiG ported
- Symptom:
/mcpnow opens at once while servers are still connecting, and connection actions (reconnect, enable, disable) run in the background so the manager stays usable. - Root cause:
- mcp/oauth/flow.go: an aborted refresh no longer falls back to a new authorization (flow.ts runFlow). - coding/mcpext: one session context replaces the generation counter; sign-ins stop with Esc and with the session, are tracked so shutdown waits for them, report aborted requests as McpSignInCancelledError, and each authorization-server request times out after 15 s (oauth.ts OAUTH_REQUEST_TIMEOUT_MS/timedFetch); pig mcp login --timeout bounds the whole sign-in (cli.ts). - /mcp opens before startup connections finish; reconnect, enable and disable run in the background with an attempt token; reconnects and replacements are serialized; direct tool calls wait for their own server; prepared…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
47c300724d(2026-10-07) - Tests:
coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthCancelsASignInWaitingOnTheAuthorizationServerWhenTheSessionShutsDown,coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthClosesTheSessionWithoutRefreshingAnExpiringToken,coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthGivesUpOnPigMcpLoginAfterTimeoutAlsoWhileTheAuthorizationServerHangs,coding/mcpext/extension_test.go::TestAgentSessionMCPHoldsTheFirstPromptForServersWithDirectToolsOnlyUpToTheStartupWait,coding/mcpext/manager_test.go::TestMcpManagerReconnectRunsInTheBackgroundWithoutAStatusScreen,coding/mcpext/manager_test.go::TestMcpManagerServerMenuAndDisable,coding/mcpext/manager_test.go::TestMcpManagerStatusScreenCancelsTheRunningOperationWithTheCancelKey,coding/mcpext/manager_test.go::TestMcpManagerStatusScreenIgnoresTheCancelKeyForOperationsThatCannotBeCancelledand 15 more in docs/bug-ledger.json - Release note:
/mcpnow opens at once while servers are still connecting, and connection actions (reconnect, enable, disable) run in the background so the manager stays usable. Reconnects and replacements are serialized, direct MCP tool calls wait for their own server's connection, and a call prepared earlier uses the server's current connection when it runs.
BF-47c3007-2
MCP OAuth sign-ins can now be cancelled with Esc at every step, session shutdown stops a running sign-in, and…
- Source: A fix Pi shipped that PiG ported
- Symptom: MCP OAuth sign-ins can now be cancelled with Esc at every step, session shutdown stops a running sign-in, and each request to the authorization server times out after 15 seconds.
- Root cause:
- mcp/oauth/flow.go: an aborted refresh no longer falls back to a new authorization (flow.ts runFlow). - coding/mcpext: one session context replaces the generation counter; sign-ins stop with Esc and with the session, are tracked so shutdown waits for them, report aborted requests as McpSignInCancelledError, and each authorization-server request times out after 15 s (oauth.ts OAUTH_REQUEST_TIMEOUT_MS/timedFetch); pig mcp login --timeout bounds the whole sign-in (cli.ts). - /mcp opens before startup connections finish; reconnect, enable and disable run in the background with an attempt token; reconnects and replacements are serialized; direct tool calls wait for their own server; prepared…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
47c300724d(2026-10-07) - Tests:
coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthCancelsASignInWaitingOnTheAuthorizationServerWhenTheSessionShutsDown,coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthClosesTheSessionWithoutRefreshingAnExpiringToken,coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthGivesUpOnPigMcpLoginAfterTimeoutAlsoWhileTheAuthorizationServerHangs,coding/mcpext/manager_test.go::TestMcpManagerReconnectRunsInTheBackgroundWithoutAStatusScreen,coding/mcpext/manager_test.go::TestMcpManagerServerMenuAndDisable,coding/mcpext/manager_test.go::TestMcpManagerStatusScreenCancelsTheRunningOperationWithTheCancelKey,coding/mcpext/manager_test.go::TestMcpManagerStatusScreenIgnoresTheCancelKeyForOperationsThatCannotBeCancelled,coding/mcpext/manager_test.go::TestMcpManagerViewCutsRowsToTheWidthand 14 more in docs/bug-ledger.json - Release note: MCP OAuth sign-ins can now be cancelled with Esc at every step, session shutdown stops a running sign-in, and each request to the authorization server times out after 15 seconds. An aborted token refresh no longer falls back to a new authorization.
BF-47c3007-3
Shutdown no longer waits up to 15 seconds to refresh an MCP OAuth token that was about to expire, only to…
- Source: A fix Pi shipped that PiG ported
- Symptom: Shutdown no longer waits up to 15 seconds to refresh an MCP OAuth token that was about to expire, only to close the server's session.
- Root cause:
- mcp/oauth/flow.go: an aborted refresh no longer falls back to a new authorization (flow.ts runFlow). - coding/mcpext: one session context replaces the generation counter; sign-ins stop with Esc and with the session, are tracked so shutdown waits for them, report aborted requests as McpSignInCancelledError, and each authorization-server request times out after 15 s (oauth.ts OAUTH_REQUEST_TIMEOUT_MS/timedFetch); pig mcp login --timeout bounds the whole sign-in (cli.ts). - /mcp opens before startup connections finish; reconnect, enable and disable run in the background with an attempt token; reconnects and replacements are serialized; direct tool calls wait for their own server; prepared…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
47c300724d(2026-10-07) - Tests:
coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthCancelsASignInWaitingOnTheAuthorizationServerWhenTheSessionShutsDown,coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthClosesTheSessionWithoutRefreshingAnExpiringToken,coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthGivesUpOnPigMcpLoginAfterTimeoutAlsoWhileTheAuthorizationServerHangs,coding/mcpext/manager_test.go::TestMcpManagerReconnectRunsInTheBackgroundWithoutAStatusScreen,coding/mcpext/manager_test.go::TestMcpManagerServerMenuAndDisable,coding/mcpext/manager_test.go::TestMcpManagerStatusScreenCancelsTheRunningOperationWithTheCancelKey,coding/mcpext/manager_test.go::TestMcpManagerStatusScreenIgnoresTheCancelKeyForOperationsThatCannotBeCancelled,coding/mcpext/manager_test.go::TestMcpManagerViewCutsRowsToTheWidthand 14 more in docs/bug-ledger.json - Release note: Shutdown no longer waits up to 15 seconds to refresh an MCP OAuth token that was about to expire, only to close the server's session. Closing a streamable HTTP session reuses the token of the last request.
BF-48318eb
fix(hljs): a resumed run counts the scan iterations before its checkpoint
- Source: PiG's own defect
- Symptom: fix(hljs): a resumed run counts the scan iterations before its checkpoint
- Root cause: A resumed run started counting at 0, so a whole run could throw (Pi prints the escaped code) where the resumed run did not (PiG printed highlighted code); maxCheckpointIterations only narrowed that window. A checkpoint now records the iterations before it, ResumePoint carries them, tui's resume state keeps them, and the resumed run continues the count, so both runs see the same counter at every match. maxCheckpointIterations goes: points past it are now exact.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
48318ebda2(2026-10-11) - Tests:
tui/highlight_resume_test.go::TestStreamedHighlightEqualsAWholeRun,tui/internal/hljs/resume_test.go::TestHighlightResumableEqualsAFreshRun,tui/internal/hljs/resume_test.go::TestHighlightResumableFollowsLaterText,tui/internal/hljs/resume_test.go::TestResumedRunCountsTheIterationsOfAWholeRun
BF-48435cd
A parsed http, https, ws, wss or ftp URL reports its fragment percent-encoded, as Node's url.hash does
- Source: PiG's own defect
- Symptom: A parsed http, https, ws, wss or ftp URL reports its fragment percent-encoded, as Node's
url.hashdoes. - Root cause: compareParseWithNode checked only protocol, hostname and search.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
48435cd213(2026-10-10) - Tests:
internal/nodeurl/url_hash_test.go::TestParseURLHashIsTheEncodedFragment - Release note: A parsed http, https, ws, wss or ftp URL reports its fragment percent-encoded, as Node's
url.hashdoes.
BF-486aee5
fix(sdk): the Go SDK ends the connection on a frame it cannot decode
- Source: PiG's own defect
- Symptom: fix(sdk): the Go SDK ends the connection on a frame it cannot decode
- Root cause: readLoop skipped a malformed frame (continue), so a request that frame carried never got an answer and the host waited on a lease. The Node, Python and Rust SDKs end the connection on such a frame (FrameBuffer destroy, json.loads and serde_json::from_slice errors), as does the host's reader. TestMalformedFrameEndsTheConnection was red (connection still open after 10 s). The other three SDKs' behavior is from reading their code, not a cross-SDK row.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
486aee5da1(2026-10-10) - Tests:
extensions/sdk/malformed_frame_test.go::TestMalformedFrameEndsTheConnection
BF-488a47a
chord/host: ProcessLock no longer fails with invalid argument on macOS and the BSDs when the temporary…
- Source: PiG's own defect
- Symptom:
chord/host:ProcessLockno longer fails withinvalid argumenton macOS and the BSDs when the temporary directory is too deep for a socket path (sun_path holds 104 bytes); the lock socket then lives in a private per-user directory,/tmp/chord2-<uid>(Pi cd6b2a55). - Root cause: A deep TMPDIR made the lock socket path exceed 104 bytes and listen failed with EINVAL. lockSocketPath falls back to the private per-user directory /tmp/chord2-<uid> when the path does not fit, and refuses a directory that is not ours or is open to others with unavailable, as Pi's manager.ts lockSocketPath does. The new processLock case of manager.test.ts is TestProcessLockAdmitsOneHolderWhenTheTemporaryDirectoryIsTooDeepForASocketPath.
- User impact: An operation failed or was rejected
- Platform: macOS
- Fixed in: 0.5.0
- Fix commits:
488a47a47d(2026-10-13) - Tests:
chord/host/manager_upstream_test.go::TestProcessLock,chord/host/manager_upstream_test.go::TestProcessLockAdmitsOneHolderWhenTheTemporaryDirectoryIsTooDeepForASocketPath,chord/host/manager_worker_unix_test.go::TestLockSocketPathKeepsTheSocketPathWithinSunPath - Release note:
chord/host:ProcessLockno longer fails withinvalid argumenton macOS and the BSDs when the temporary directory is too deep for a socket path (sun_path holds 104 bytes); the lock socket then lives in a private per-user directory,/tmp/chord2-<uid>(Pi cd6b2a55).
BF-48a28fe
Models an extension's provider object returns keep the member order the extension wrote them in, as Pi keeps…
- Source: PiG's own defect
- Symptom: Models an extension's provider object returns keep the member order the extension wrote them in, as Pi keeps the returned objects, instead of being sorted by member name.
- Root cause: decodeModels re-encoded every record through a map, which sorted its members. The stored record keeps the bytes it is given, so models an extension's provider object returned reached the catalog (and later filterModels calls) in sorted order. Keep the record as written and append the provider id only when the record has none.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
48a28fe06a(2026-10-13),7b0f5d7da8(2026-10-13) - Tests:
coding/extension/host/subprocess/native_provider_models_test.go::TestNativeProviderModelsKeepTheirMemberOrder,internal/codingagent/native_provider_filter_order_test.go::TestNativeProviderFilterAllModelsSeesTheProvidersOwnModels - Release note: Models an extension's provider object returns keep the member order the extension wrote them in, as Pi keeps the returned objects, instead of being sorted by member name. A model without a
providergets the provider's id after its own members.
BF-495f017
A bashExecution entry records excludeFromContext: false when the caller passed false, as Pi does; an…
- Source: PiG's own defect
- Symptom: A
bashExecutionentry recordsexcludeFromContext: falsewhen the caller passedfalse, as Pi does; an RPCbashcommand without the member records none. - Root cause: messages.ts:39 declares the member optional and agent-session.ts:3889 assigns options?.excludeFromContext, so the member is present exactly when a boolean was passed: interactive ! commands write false (interactive-mode.ts:7029,7058), an RPC bash command without the member writes none (rpc-mode.ts:571,577). PiG's bool+omitempty dropped false. The field is now *bool through the entry, the Session recording API and the RPC command. Found by the bash journey (session file diff).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
495f01780d(2026-10-09),4a0bc3d84d(2026-10-09),eb6ae90e7d(2026-10-09) - Tests:
internal/codingagent/interactive_user_bash_footer_test.go::TestInteractiveBashRefreshesTheFooterContextUsage,internal/codingagent/output_pad_blocks_test.go::TestResumedBranchSummaryHasPiSpacingAboveItsLabel,internal/sessionentry/entry_test.go::TestBashExecutionEntryKeepsExcludeFromContextFalseAndOmitsUnset,internal/sessionentry/entry_test.go::TestBranchSummaryEntryWritesFromHookFalseInPiMemberOrder - Release note: A
bashExecutionentry recordsexcludeFromContext: falsewhen the caller passedfalse, as Pi does; an RPCbashcommand without the member records none. The Go fields are*bool.
BF-495f017-2
A branch-summary entry writes fromHook: false in Pi's member order, and the summary has its Spacer
- Source: PiG's own defect
- Symptom: A branch-summary entry writes
fromHook: falsein Pi's member order, and the summary has its Spacer. - Root cause: messages.ts:39 declares the member optional and agent-session.ts:3889 assigns options?.excludeFromContext, so the member is present exactly when a boolean was passed: interactive ! commands write false (interactive-mode.ts:7029,7058), an RPC bash command without the member writes none (rpc-mode.ts:571,577). PiG's bool+omitempty dropped false. The field is now *bool through the entry, the Session recording API and the RPC command. Found by the bash journey (session file diff).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
495f01780d(2026-10-09),6935d5ecea(2026-10-09),eb6ae90e7d(2026-10-09) - Tests:
internal/codingagent/interactive_compaction_notice_test.go::TestSessionCompactedStatusIsShownByInitialRenderOnly,internal/codingagent/output_pad_blocks_test.go::TestResumedBranchSummaryHasPiSpacingAboveItsLabel,internal/sessionentry/entry_test.go::TestBashExecutionEntryKeepsExcludeFromContextFalseAndOmitsUnset,internal/sessionentry/entry_test.go::TestBranchSummaryEntryWritesFromHookFalseInPiMemberOrder - Release note: A branch-summary entry writes
fromHook: falsein Pi's member order, and the summary has its Spacer. "Session compacted N time(s)" is shown only when a session is opened or its transcript is rebuilt.
BF-4a0bc3d
The footer context percentage is refreshed after a ! command is recorded, as Pi's footer asks the session…
- Source: PiG's own defect
- Symptom: The footer context percentage is refreshed after a
!command is recorded, as Pi's footer asks the session on every render. - Root cause: Pi's footer asks session.getContextUsage() on every render (footer.ts:143) and the estimate counts a bashExecution message (compaction.ts:337-340), so a recorded ! command moves the percentage at once. PiG keeps a stored estimate that only agent events and transcript rebuilds refreshed.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
4a0bc3d84d(2026-10-09) - Tests:
internal/codingagent/interactive_user_bash_footer_test.go::TestInteractiveBashRefreshesTheFooterContextUsage - Release note: The footer context percentage is refreshed after a
!command is recorded, as Pi's footer asks the session on every render.
BF-4ac21c8
fix(jsloop): run a tick whose tool call already ended at once, though no wait is open
- Source: PiG's own defect
- Symptom: fix(jsloop): run a tick whose tool call already ended at once, though no wait is open
- Root cause: The host sees no wait while an extension tool awaits its own timer, so a tick of an ended tool call that reached the host late was held for the next turn and written behind agent_settled. The runtime marks a call tick only while a sibling tool still runs, so the loop turns: the call runs at once, behind the callbacks held before it.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
4ac21c86c4(2026-10-21) - Tests:
coding/extension/host/subprocess/timer_turns_test.go::TestNodeTickCallsOfAToolRunAfterItsEndWhenTheyArriveLate,internal/jsloop/turns_test.go::TestTurnsRunALateTickAtOnceWhileNoWaitIsOpen
BF-4afdbac
ProjectTrustStore reads entries in JavaScript order: readTrustFile named whichever invalid value Go's map…
- Source: PiG's own defect
- Symptom:
ProjectTrustStorereads entries in JavaScript order:readTrustFilenamed whichever invalid value Go's map iteration reached first, so a trust.json with several wrong-typed values reported a random key; Pi'sObject.entriesorder (array-index keys, then insertion order) decides, andreadnow walksorderedjsonkeys. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
4afdbac644(2026-10-10) - Tests:
internal/codingagent/trust_store_multi_oracle_test.go::TestProjectTrustStoreMultiEntryFilesMatchPi - Release note:
ProjectTrustStorereads entries in JavaScript order:readTrustFilenamed whichever invalid value Go's map iteration reached first, so a trust.json with several wrong-typed values reported a random key; Pi'sObject.entriesorder (array-index keys, then insertion order) decides, andreadnow walksorderedjsonkeys. New live-Pi oracleTestProjectTrustStoreMultiEntryFilesMatchPi(internal/codingagent/trust_store_multi_oracle_test.go; 1,200 trust.json files x 1-5 get/getEntry/set/setMany operations; symlinked cwd, missing paths, astral and BMP-high keys); red before the fix (4-5 of 1,200, varying with map order); 8 mutations red (key order, null rejected, false-as-true, non-object check, UTF-16 key sort, null entry stops the ancestor walk, null delete, BOM). ExtendsTestProjectTrustStoreMatchesPi, which passed before the fix. Not compared: the V8 text afterFailed to read trust store <path>:, file modes, cross-process locking.
BF-4bcc7da
StreamProxy merges the final tool call and reports abort and timeout as Pi does, and it no longer changes a…
- Source: PiG's own defect
- Symptom:
StreamProxymerges the final tool call and reports abort and timeout as Pi does, and it no longer changes a delivered message when the server sends more after the terminal event. - Root cause: PiG compared the text after each input and notified once. - A chunk that carries the paste start marker discards any earlier paste buffer, removes the first marker and joins surrounding text into the pasted content (editor.ts:720-724); PiG processed the text before the marker as typed input and kept the old buffer. - Jump mode takes decodePrintableKey(data) before raw data (editor.ts:707); PiG inserted CSI-u letters as jump targets. After: all 700 sequences match; a 4000-sequence run with another seed also matches.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
53dccd580f(2026-10-05),e2e8d50d71(2026-10-05),f38ab7ac87(2026-10-05),18fb7dd7d4(2026-10-06),4bcc7da691(2026-10-06),9c8d8439aa(2026-10-06),e2cde42910(2026-10-06) - Tests:
agent/new_agent_stream_error_test.go::TestNewAgentAcceptsEachStreamFunctionSource,agent/new_agent_stream_error_test.go::TestNewAgentFailsWithoutAStreamFunction,agent/new_agent_stream_error_test.go::TestSendWithoutAStreamFunctionReportsTheFailure,agent/proxy_oracle_test.go::TestStreamProxyEventAfterDoneIsNotDelivered,agent/proxy_oracle_test.go::TestStreamProxyMalformedLineAfterDoneLeavesTheResult,internal/nativeplatform/clipboard_native_roundtrip_test.go::TestNativeClipboardTextRoundTrip,tui/editor_oracle_test.go::TestEditorMatchesPiOracle,tui/main_screen_image_row_test.go::TestMainScreenEditBelowImageRowDoesNotClearand 19 more in docs/bug-ledger.json - Release note:
StreamProxymerges the final tool call and reports abort and timeout as Pi does, and it no longer changes a delivered message when the server sends more after the terminal event. The stdin buffer drops an incomplete sequence before a bracketed paste, the editor callsonChangeas Pi does, and overlays hide the cursor and request a frame as Pi does.
BF-4c45afa
fix(sdk-py): stage pig_sdk.tui with the package, which pig_sdk imports at load; keep the pi-ai patterns…
- Source: PiG's own defect
- Symptom: fix(sdk-py): stage pig_sdk.tui with the package, which pig_sdk imports at load; keep the pi-ai patterns gofmt-clean
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
4c45afaf73(2026-10-21) - Tests:
extensions/sdk-py/bundle_test.go::TestEveryPackageSourceIsBundled
BF-4c7ccf2
A classifier response that reports more than 2,147,483,647 input or output tokens keeps the reported count…
- Source: PiG's own defect
- Symptom: A classifier response that reports more than 2,147,483,647 input or output tokens keeps the reported count and its cost, as Pi does, instead of capping it.
- Root cause: classifier-shared.ts tokenCount keeps the response's number, so a count past 2^31-1 (3000000000 input tokens) is that count; classifierTokenCount capped it at MaxInt32. It now caps at Number.MAX_SAFE_INTEGER, so every whole count a JS number holds exactly is kept.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
4c7ccf231e(2026-10-13) - Tests:
ai/classifier_shared_exports_test.go::TestParseClassifierUsagePricesTheModelCatalog - Release note: A classifier response that reports more than 2,147,483,647 input or output tokens keeps the reported count and its cost, as Pi does, instead of capping it.
BF-4d04134
The cancel hint of extension loaders (/share, /bug) shows the key bound to tui.select.cancel instead of…
- Source: PiG's own defect
- Symptom: The cancel hint of extension loaders (
/share,/bug) shows the key bound totui.select.cancelinstead of alwaysescape/ctrl+c. - Root cause: (1) tui.input.submit (ctrl+s here) must select through the search Input's onSubmit -> selectList.handleInput("\r"); Pig's host-owned TextInput only marked itself done. (2) Pig re-fired onSelect/onCancel on every later navigation key because it read the list's sticky Done state; Pi fires once per confirm/cancel key (new FilterableList.Reset, called by the selector after it dispatches). Scenario settings/06 (/thinking, m Down Left d -> '> dm') is red on the old selector ('> md') and byte-equal now.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d1f258f335(2026-10-06),4d041349a7(2026-10-07),ccaade27af(2026-10-07) - Tests:
internal/codingagent/thinking_selector_oracle_test.go::TestThinkingSelectorInputMatchesPi,test/parity/scenarios/settings/06-thinking-selector-search-editing.toml,test/parity/scenarios/settings/08-settings-search-editing.toml,tui/bordered_loader_oracle_test.go::TestBorderedLoaderMatchesPi,tui/cancellable_loader_input_test.go::TestCancellableLoaderCancelFollowsTheBindingAndCallsOnAbortEachPress,tui/cancellable_loader_input_test.go::TestCancellableLoaderCancelWithoutOnAbortOnlyAborts,tui/settings_list_input_test.go::TestSettingsListAnyDelegatedKeyResetsTheSelectionToTheFirstMatch,tui/settings_list_input_test.go::TestSettingsListBoundActionsRunInUpstreamOrderand 5 more in docs/bug-ledger.json - Release note: The cancel hint of extension loaders (
/share,/bug) shows the key bound totui.select.cancelinstead of alwaysescape/ctrl+c.
BF-4d33f78
fix(tui): a tool error result collapses like any other (Pi updateResult)
- Source: PiG's own defect
- Symptom: fix(tui): a tool error result collapses like any other (Pi updateResult)
- Root cause: tool-execution.ts updateResult never changes the expansion; bash/read/write renderers show a collapsed preview for an error as for a success. Found by e2e runs of scripted replies through real Pi and Pig (TUI panes and session files compared). TestErrorResultStaysCollapsedUntilExpanded is red without the change. NOTE: TestRefreshModelRuntimeHonoursNetworkForceErrorsAndRegistrationCover fails ('TempDir RemoveAll cleanup: directory not empty') at -count>1 and in full-package runs on the integrate base, unrelated.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
4d33f78449(2026-10-11) - Tests:
internal/codingagent/model_registry_availability_refresh_gaps_test.go::TestRefreshModelRuntimeHonoursNetworkForceErrorsAndRegistrationCover,tui/tool_execution_test.go::TestErrorResultStaysCollapsedUntilExpanded
BF-4d965e3
The /tree label of a custom tool call shows its arguments in the order the model sent them, with <, >…
- Source: PiG's own defect
- Symptom: It used to sort the members and could cut a multi-byte character.
- Root cause: tree-selector.ts:996-999 writes JSON.stringify(args).slice(0, 40) and "..." when the whole text is longer. Pig encoded the decoded map, which sorted the members, and cut at 40 bytes, which could split a multi-byte character into invalid UTF-8. The label now uses ToolCall.ArgumentsJSON (the model's member order), jsonstringify.Canonicalize and UTF-16 length and slicing. The truncation case asserts the exact label instead of only its byte length. Regression: TestFormatToolCall_CustomToolMatchesJSONStringify (red before).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
4d965e37c0(2026-10-06) - Tests:
internal/codingagent/tree_row_format_test.go::TestFormatToolCall_CustomToolMatchesJSONStringify,internal/codingagent/tree_row_format_test.go::TestFormatToolCall_PerToolFormatters,internal/codingagent/tree_row_format_test.go::TestFormatTreeRow_AllEntryTypes - Release note: The
/treelabel of a custom tool call shows its arguments in the order the model sent them, with<,>and&literal, and truncates at 40 UTF-16 units as Pi does. It used to sort the members and could cut a multi-byte character.
BF-4e204f4
fix(sdk-py): clear bandit's findings in the 0.5.2 Python SDK code
- Source: PiG's own defect
- Symptom: fix(sdk-py): clear bandit's findings in the 0.5.2 Python SDK code
- Root cause: BuiltinTool.execute checked its spec with assert, which python -O strips; it now raises RuntimeError instead.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
4e204f4f23(2026-10-22) - Tests: None recorded.
BF-4e47260
A lazy line joins a list item as written (indentation kept), four spaces after the marker make the first line…
- Source: PiG's own defect
- Symptom: A lazy line joins a list item as written (indentation kept), four spaces after the marker make the first line indented code that no lazy line continues, the break test uses the content indent to limit rules/headings/fences, a blank-after-slice line no longer ends the item, a lazy line inside an item's fence stays in the fence, and a LaTeX block that takes the end of the text leaves no trailing space row.
- Root cause: TestParseMatchesTheLibraryOnMutatedCorpus and TestLexerMatchesTheLibraryOnMutatedCorpus compare eemeli/yaml 2.9.0 with Parse/lex on 6000 mutated corpus texts (40 differed before, 15-17 per 20000 after the first fixes). Fixed: an unterminated quote (buffer.substring(0, -1) is empty: the error is at the end of input, not after the first line); a line starting with a non-ASCII character reduced the continuation indent because the lexer looked at the second byte, not the second UTF-16 unit (charAt(1)); Invalid escape sequence raw text and the 1024-unit implicit key limit counted bytes; anchors reject JavaScript whitespace (RE2 \s is narrower); -0 is 0 as an omap/set key; Buffer.from(.., base64)…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
125d9e37a8(2026-10-09),2b3403045b(2026-10-09),2a919a4c3c(2026-10-10),4e472601e7(2026-10-10),4e5b684ecd(2026-10-10),5b4a3d393e(2026-10-10),ac0dc3bee4(2026-10-10),b5b7b43f82(2026-10-10),cdf0be76e4(2026-10-10),ffcd800f73(2026-10-10) - Tests:
ai/anthropic_setup_oracle_test.go::TestAnthropicMessagesAPISetupMatchesPi,internal/codingagent/session_file_bytes_oracle_test.go::TestSessionFileBytesMatchPi,internal/yaml12/parse_mutation_test.go::TestLexerMatchesTheLibraryOnMutatedCorpus,internal/yaml12/parse_mutation_test.go::TestParseMatchesTheLibraryOnMutatedCorpus,internal/yaml12/parse_mutation_test.go::TestParseMatchesTheLibraryOnUTF16Offsets,internal/yaml12/parse_test.go::TestParseMatchesTheLibrary,tui/markdown_render_oracle_test.go::TestMarkdownRenderMatchesPi - Release note: A lazy line joins a list item as written (indentation kept), four spaces after the marker make the first line indented code that no lazy line continues, the break test uses the content indent to limit rules/headings/fences, a blank-after-slice line no longer ends the item, a lazy line inside an item's fence stays in the fence, and a LaTeX block that takes the end of the text leaves no trailing space row.
BF-4e5b684
A list item whose first line is another list item is rendered as a nested list with no bullet of its own, and…
- Source: PiG's own defect
- Symptom: A list item whose first line is another list item is rendered as a nested list with no bullet of its own, and an indented line that lazily continues a quoted paragraph loses up to four leading spaces, as in Pi.
- Root cause: marked lexes a list item's text as blocks, so '- + a' is a list in a list item and Pi's renderList prints no bullet for an item whose first token is a list (markdown.ts renderList); the blockquote tokenizer removes up to four spaces (codeRemoveIndent) from an indented lazy line it joins to the paragraph. 9 new rows in TestMarkdownRenderMatchesPi (9 red before).
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
4e5b684ecd(2026-10-10),cdf0be76e4(2026-10-10) - Tests:
tui/markdown_render_oracle_test.go::TestMarkdownRenderMatchesPi - Release note: A list item whose first line is another list item is rendered as a nested list with no bullet of its own, and an indented line that lazily continues a quoted paragraph loses up to four leading spaces, as in Pi.
BF-4e8c517
fix(runtimecell): keep the Python runner's _run callable without the built event
- Source: PiG's own defect
- Symptom: fix(runtimecell): keep the Python runner's _run callable without the built event
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
4e8c517e40(2026-10-21) - Tests: None recorded.
BF-4f079a7
An MCP request over Streamable HTTP that is cancelled or times out while the server's authorization is being…
- Source: A fix Pi shipped that PiG ported
- Symptom: An MCP request over Streamable HTTP that is cancelled or times out while the server's authorization is being refreshed is no longer sent again after the refresh; other requests waiting on the same refresh still complete.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
4f079a786c(2026-10-12) - Tests:
mcp/streamable_http_send_signal_test.go::TestStreamableHTTPTransportDoesNotRetryRequestsThatWereCancelledWhileAuthorizationWasRecovering - Release note: An MCP request over Streamable HTTP that is cancelled or times out while the server's authorization is being refreshed is no longer sent again after the refresh; other requests waiting on the same refresh still complete.
BF-4f6992d
Durable: a tool's PrepareArguments result of a typed object (a struct, or a map of another element type)…
- Source: PiG's own defect
- Symptom: Durable: a tool's
PrepareArgumentsresult of a typed object (a struct, or a map of another element type) reaches validation and execution as that object; it reached them as no arguments. - Root cause: Pi harness/tool.ts:138-145 prepare returns
{ args: tool.prepareArguments(args) as JsonObject }without checking the value, and validate (tool.ts:148-154) hands it to validateToolArguments, whose error text ends withReceived arguments:\n${JSON.stringify(toolCall.arguments, null, 2)}(ai/src/utils/validation.ts:347). - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
19e834a869(2026-10-11),4f6992d663(2026-10-11) - Tests:
durable/core/driver/tool_intent_pi_test.go::TestCoreToolRecordsItsPreparedArgumentsAndDefaultReplayBeforeExecuting,durable/core/driver/tool_intent_pi_test.go::TestCoreToolRefusesANonObjectPreparedResultAsInvalidArguments,durable/core/turn/generation_test.go::TestToolBlockedAndInvalid - Release note: Durable: a tool's
PrepareArgumentsresult of a typed object (a struct, or a map of another element type) reaches validation and execution as that object; it reached them as no arguments.
BF-4f6992d-2
Durable: a PrepareArguments result that is not an object fails validation as an invalid_arguments tool…
- Source: PiG's own defect
- Symptom: Durable: a
PrepareArgumentsresult that is not an object fails validation as aninvalid_argumentstool error whoseReceived argumentsshows the returned value, as Pi does; it panicked the core. - Root cause: Pi harness/tool.ts:138-145 prepare returns
{ args: tool.prepareArguments(args) as JsonObject }without checking the value, and validate (tool.ts:148-154) hands it to validateToolArguments, whose error text ends withReceived arguments:\n${JSON.stringify(toolCall.arguments, null, 2)}(ai/src/utils/validation.ts:347). - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
19e834a869(2026-10-11),4f6992d663(2026-10-11) - Tests:
durable/core/driver/tool_intent_pi_test.go::TestCoreToolRecordsItsPreparedArgumentsAndDefaultReplayBeforeExecuting,durable/core/driver/tool_intent_pi_test.go::TestCoreToolRefusesANonObjectPreparedResultAsInvalidArguments,durable/core/turn/generation_test.go::TestToolBlockedAndInvalid - Release note: Durable: a
PrepareArgumentsresult that is not an object fails validation as aninvalid_argumentstool error whoseReceived argumentsshows the returned value, as Pi does; it panicked the core.
BF-4fab2ef
Durable error values (FileError, ExecutionError, JsonlCorruptionError, ReadAfterWrite, StorageRejected…
- Source: PiG's own defect
- Symptom: Durable error values (FileError, ExecutionError, JsonlCorruptionError, ReadAfterWrite, StorageRejected, ConversationBusy) report Pi's
nameand a V8-stylestackwhen built by their constructors; the JSONL corruption constructor is now exported as NewJsonlCorruptionError. - Root cause: FileError, ExecutionError (env), JsonlCorruptionError (jsonl), ReadAfterWrite, StorageRejected and ConversationBusy now have Name() and a Stack captured by their constructors, headed '<name>: <message>' as the JSONL poisoned error already does (precedent). newCorruption is exported as NewJsonlCorruptionError (Pi's exported class constructor; 27 internal call sites renamed), closing its no-go-symbol row. Tests: TestJsonlCorruptionErrorKeepsItsMessageCauseNameAndStack, TestDurableErrorsHaveNameAndStack, TestFileAndExecutionErrorsHaveNameAndStack (names, messages, unwrap, stack header and call site; removing a Stack assignment or Name fails them).
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
4fab2efaa0(2026-10-07) - Tests: None recorded.
- Release note: Durable error values (FileError, ExecutionError, JsonlCorruptionError, ReadAfterWrite, StorageRejected, ConversationBusy) report Pi's
nameand a V8-stylestackwhen built by their constructors; the JSONL corruption constructor is now exported as NewJsonlCorruptionError.
BF-4fc8c2e
With an active Piglet, an extension that denies every tool with SetActiveTools([]) is no longer widened…
- Source: PiG's own defect
- Symptom: With an active Piglet, an extension that denies every tool with
SetActiveTools([])is no longer widened back to the Piglet's full tool scope. - Root cause: …ts scope
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
4fc8c2e65f(2026-10-06, #169) - Tests:
coding/extension/host/subprocess/state_presence_test.go::TestNodeActiveToolsNarrowedToNoneSurvivesTheWire,coding/extension/host/subprocess/state_test.go::TestActiveToolsNarrowedToNoneIsAnEmptyListOnTheWire,coding/extension/host/subprocess/state_test.go::TestSetActiveToolsEmptyListReachesTheHostAsEmpty,coding/piglet/empty_active_scope_test.go::TestPigletScopeNeverWidensWhatAnExtensionNarrowed,coding/session_prompt_defaults_test.go::TestActiveToolNamesAfterDeactivatingAllToolsIsNonNilEmpty,extensions/sdk/getter_state_test.go::TestContextGettersDistinguishAbsentEmptyAndFailure - Release note: With an active Piglet, an extension that denies every tool with
SetActiveTools([])is no longer widened back to the Piglet's full tool scope. The Piglet scope only narrows what an earlier extension selected.getActiveToolsreplies and the replicated state carry[]for a session narrowed to no tools, nevernull.
BF-4fe779e
A /reload that arrived while the supervisor was restarting a crashed isolated extension could end the…
- Source: PiG's own defect
- Symptom: A
/reloadthat arrived while the supervisor was restarting a crashed isolated extension could end the process the reloaded extension runs in, so its next call failed with a reset or closed connection. - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
4fe779e53e(2026-10-20, #236) - Tests:
coding/extension/host/subprocess/retained_factory_test.go::TestNodeIsolatedCrashAfterRetainedReloadRestarts,coding/extension/host/subprocess/retained_factory_test.go::TestNodeIsolatedReloadWaitsForCrashRestart - Release note: A
/reloadthat arrived while the supervisor was restarting a crashed isolated extension could end the process the reloaded extension runs in, so its next call failed with a reset or closed connection. The restart now runs under the same lock as load and reload: a reload waits for the restart to register, then retains the restarted process; a restart that a reload or load already replaced does nothing.
BF-50233fd
An env SSH command that runs past its timeout now receives SIGTERM, as in Pi, instead of SIGKILL, and the…
- Source: PiG's own defect
- Symptom: An
envSSH command that runs past its timeout now receives SIGTERM, as in Pi, instead of SIGKILL, and the operation fails as soon as the timeout expires, even when a process started bysshkeeps its output open. - Root cause: Pi's runSsh timer (ssh.ts:136-146) calls child.kill(), Node's SIGTERM, and rejects at once with the stderr read so far. PiG's runSsh killed the child with SIGKILL through exec.CommandContext and returned only after cmd.Run finished, up to WaitDelay (5 s) later when a descendant kept stdout or stderr open. runSsh now sets Cmd.Cancel to send SIGTERM (a plain kill on Windows, as Node does), waits on a goroutine and returns when the timer fires; the goroutine reaps the child, still bounded by WaitDelay, and stderr is collected in a locked buffer.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d4e4a82ed9(2026-10-08),50233fd0d4(2026-10-10) - Tests:
coding/cli/session_replacement_interactive_test.go::TestInteractiveExtensionNewSessionKeepsTheCallerAliveUntilItReturns,coding/cli/session_resume_ui_test.go::TestInteractiveResumeIntoAnUntrustedProjectPromptsForTrust,coding/cli/session_resume_ui_test.go::TestInteractiveResumeWithAMissingCWDShowsPisStatuses,env/ssh_known_hosts_semantics_test.go::TestConcurrentHostKeyChangesToOneFileAreAppliedOneAtATime,env/ssh_timeout_test.go::TestRunSshTimeoutTerminatesTheChildAndFailsWithoutWaitingForItsOutputToClose,internal/codingagent/interactive_runtime_host_test.go::TestNewInteractiveModeRoutesSessionWorkThroughItsRuntimeHost,internal/codingagent/interactive_runtime_host_test.go::TestNewInteractiveModeWithoutRuntimeHostCannotImport - Release note: An
envSSH command that runs past its timeout now receives SIGTERM, as in Pi, instead of SIGKILL, and the operation fails as soon as the timeout expires, even when a process started bysshkeeps its output open.
BF-504869b
The MCP client uses JavaScript's trim() and \s where Pi does: in stdio and Streamable HTTP blank-line and…
- Source: PiG's own defect
- Symptom: The MCP client uses JavaScript's
trim()and\swhere Pi does: in stdio and Streamable HTTP blank-line and blank-event checks, HTTP error bodies, theinsufficient_scopechallenge check and theoffline_accessconsent check. - Root cause: streamable-http.ts:163 matches /(?:^|[\s,])error="?insufficient_scope"?/i and flow.ts:197 asks for consent when scope.split(/\s+/) includes offline_access. JavaScript's \s includes U+00A0, U+FEFF and the Unicode spaces and excludes U+0085; RE2's \s is ASCII only and strings.Fields splits at U+0085 and not at U+FEFF. A 403 whose challenge separated the field with a no-break space was not treated as step-up authorization, and a scope joined with a BOM or ending in U+0085 got the wrong prompt. The regex now uses the JS class (as mcp/oauth/discovery.go already does) and StartAuthorization uses scopeFields (as flow.go:553 already does).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
504869b911(2026-10-09),97493667e3(2026-10-09),abf52e2a35(2026-10-09),c0020ece56(2026-10-09),ceadbf354e(2026-10-09) - Tests:
coding/mcpext/oauth_challenge_latin1_test.go::TestMcpAuthProviderParsesTheChallengeHeaderAsLatin1,coding/mcpext/oauth_expire_at_test.go::TestMCPOAuthCredentialStoreSavesATokenExpiryThatOverflowsAsNull,durable/env/node/find_bash_trim_test.go::TestFindBashOnPathTrimsTheAnswerLikeJavaScript,durable/harness/js_trim_test.go::TestSummaryTextTrimsLikeJavaScript,env/js_trim_test.go::TestAcceptHostKeyStoresTheLineTrimmedLikeJavaScript,env/js_trim_test.go::TestCheckDaemonTrimsTheAnswerLikeJavaScript,env/js_trim_test.go::TestNonBlankLinesTrimsLikeJavaScript,env/js_trim_test.go::TestParseProbeReportsTheAnswerTrimmedLikeJavaScriptand 16 more in docs/bug-ledger.json - Release note: The MCP client uses JavaScript's
trim()and\swhere Pi does: in stdio and Streamable HTTP blank-line and blank-event checks, HTTP error bodies, theinsufficient_scopechallenge check and theoffline_accessconsent check. The env SSH helpers and the durable compaction summary also trim as Pi does. A byte-order mark counts as whitespace and U+0085 does not.
BF-504869b-2
MCP response headers are read as Node's fetch returns them, one Latin-1 character per byte
- Source: PiG's own defect
- Symptom: MCP response headers are read as Node's
fetchreturns them, one Latin-1 character per byte. - Root cause:
- mcp/oauth: an expires_in that overflows Date.now() + expires_in * 1000 to Infinity made json.Marshal fail after TokensExpireAt became *float64, so SaveTokens on the credential store failed the sign-in. Pi's JSON.stringify writes null and reads it back as expired; structuredClone keeps Infinity in memory. McpOAuthState now writes null, reads null as 0 and Clone keeps the float. The insufficient_scope test expected a UTF-8 BOM to separate the field, which Pi does not do. jsstring.FromLatin1 now decodes Content-Type and WWW-Authenticate at every reader (needsAuthorization, responseContentType, McpAuthRequiredError, both OnUnauthorized). - mcp: a number id keys Maps by SameValueZero and…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
504869b911(2026-10-09),abf52e2a35(2026-10-09) - Tests:
coding/mcpext/oauth_challenge_latin1_test.go::TestMcpAuthProviderParsesTheChallengeHeaderAsLatin1,coding/mcpext/oauth_expire_at_test.go::TestMCPOAuthCredentialStoreSavesATokenExpiryThatOverflowsAsNull,durable/env/node/find_bash_trim_test.go::TestFindBashOnPathTrimsTheAnswerLikeJavaScript,env/js_trim_test.go::TestAcceptHostKeyStoresTheLineTrimmedLikeJavaScript,env/js_trim_test.go::TestCheckDaemonTrimsTheAnswerLikeJavaScript,env/js_trim_test.go::TestParseProbeReportsTheAnswerTrimmedLikeJavaScript,env/js_trim_test.go::TestSshErrorMessageTrimsTheDiagnosticsLikeJavaScript,internal/jsstring/latin1_test.go::TestFromLatin1DecodesEachByteAsOneCodePointand 9 more in docs/bug-ledger.json - Release note: MCP response headers are read as Node's
fetchreturns them, one Latin-1 character per byte. This applies toContent-Type,WWW-AuthenticateandMcpAuthRequiredError.WWWAuthenticate.
BF-504869b-3
A JSON-RPC number id is a JavaScript number
- Source: PiG's own defect
- Symptom: A JSON-RPC number id is a JavaScript number.
- Root cause:
- mcp/oauth: an expires_in that overflows Date.now() + expires_in * 1000 to Infinity made json.Marshal fail after TokensExpireAt became *float64, so SaveTokens on the credential store failed the sign-in. Pi's JSON.stringify writes null and reads it back as expired; structuredClone keeps Infinity in memory. McpOAuthState now writes null, reads null as 0 and Clone keeps the float. The insufficient_scope test expected a UTF-8 BOM to separate the field, which Pi does not do. jsstring.FromLatin1 now decodes Content-Type and WWW-Authenticate at every reader (needsAuthorization, responseContentType, McpAuthRequiredError, both OnUnauthorized). - mcp: a number id keys Maps by SameValueZero and…
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
504869b911(2026-10-09),c0020ece56(2026-10-09) - Tests:
coding/mcpext/oauth_challenge_latin1_test.go::TestMcpAuthProviderParsesTheChallengeHeaderAsLatin1,coding/mcpext/oauth_expire_at_test.go::TestMCPOAuthCredentialStoreSavesATokenExpiryThatOverflowsAsNull,durable/env/node/find_bash_trim_test.go::TestFindBashOnPathTrimsTheAnswerLikeJavaScript,env/js_trim_test.go::TestAcceptHostKeyStoresTheLineTrimmedLikeJavaScript,env/js_trim_test.go::TestCheckDaemonTrimsTheAnswerLikeJavaScript,env/js_trim_test.go::TestParseProbeReportsTheAnswerTrimmedLikeJavaScript,env/js_trim_test.go::TestSshErrorMessageTrimsTheDiagnosticsLikeJavaScript,internal/jsstring/latin1_test.go::TestFromLatin1DecodesEachByteAsOneCodePointand 9 more in docs/bug-ledger.json - Release note: A JSON-RPC number id is a JavaScript number. The unknown-response message renders the id as
String(id)does, and the id is written asJSON.stringifywrites it. A request with id-0is cancelled byrequestId: 0.
BF-50f526d
The Go, Rust and Python SDKs export Pi's CURRENT_SESSION_VERSION, VIRTUAL_MODEL_STATE_ENTRY…
- Source: PiG's own defect
- Symptom: The Go, Rust and Python SDKs export Pi's
CURRENT_SESSION_VERSION,VIRTUAL_MODEL_STATE_ENTRY,getLatestCompactionEntry,parseSkillBlockandformatSkillsForPrompt. - Root cause: Pi's index.ts exports CURRENT_SESSION_VERSION, VIRTUAL_MODEL_STATE_ENTRY, getLatestCompactionEntry, parseSkillBlock and formatSkillsForPrompt; only the Node SDK, which runs Pi's bundle, had them. Each SDK now has them: parseSkillBlock follows Pi's lazy pattern without a regex dependency (the content ends at the first closing tag that leaves nothing or two newlines and one more character) and trims the user message as JavaScript's trim does (U+FEFF is white space, U+0085 is not); formatSkillsForPrompt escapes the five XML characters and names the loading tool as Pi does.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
50f526d69e(2026-10-11) - Tests:
test/extension-conformance/session_skill_helpers_test.go::TestConformance_SessionAndSkillHelpersAgreeAcrossSDKs - Release note: The Go, Rust and Python SDKs export Pi's
CURRENT_SESSION_VERSION,VIRTUAL_MODEL_STATE_ENTRY,getLatestCompactionEntry,parseSkillBlockandformatSkillsForPrompt.
BF-5144418
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
5144418192(2026-10-21) - Tests: None recorded.
BF-5260e5c
The Rust SDK's get_model_info() reads Pi's maxTokens and cost (it reported 0 for the output limit and…
- Source: PiG's own defect
- Symptom: The Rust SDK's
get_model_info()reads Pi'smaxTokensandcost(it reported 0 for the output limit and every price) and returns an error for a field of the wrong type, as the Go SDK'sModelInfodoes. - Root cause: The host answers getModelInfo with Pi's Model: maxTokens is the output limit and cost holds the per-million-token prices (packages/ai/src/types.ts). The Go SDK's ModelInfo reads those, and the older flat keys, modelId, displayName and an object provider. The Rust get_model_info read only the flat keys with unwrap_or(0), so against a Pi model it reported 0 for the output limit and for all four prices: a plausible constant, not an error.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5260e5c15f(2026-10-11) - Tests:
extensions/sdk/model_info_decode_test.go::TestModelInfoDecodeMatchesTheRustSDK,test/extension-conformance/typed_model_info_test.go::TestConformance_TypedModelInfoAgreesBetweenGoAndRust - Release note: The Rust SDK's
get_model_info()reads Pi'smaxTokensandcost(it reported 0 for the output limit and every price) and returns an error for a field of the wrong type, as the Go SDK'sModelInfodoes.
BF-52858aa
setWorkingMessage with no message restores the default working label and an empty string shows an empty…
- Source: PiG's own defect
- Symptom: Both used to show the default.
- Root cause: Pi's setHiddenThinkingLabel(label?: string) shows label ?? defaultHiddenThinkingLabel (interactive-mode.ts:2383), as setWorkingMessage does. extension.HiddenThinkingLabelClearer {ClearHiddenThinkingLabel()} is the optional absent case (the bridge falls back to SetHiddenThinkingLabel("") for a UIContext without it; ExtUIContext and the in-process runner wrapper implement it); the interactive mode keeps *string (an empty label is drawn empty); Go ClearHiddenThinkingLabel, Rust clear_hidden_thinking_label, Python set_hidden_thinking_label(None); Node already omits undefined. Two existing tests asserted the old 'an empty label restores the default' and now assert the empty label and the clear.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e8cc072521(2026-10-08),682f5d7578(2026-10-09),a5d58095b0(2026-10-09),d2b0ada190(2026-10-09),da99854d1b(2026-10-09),45254d4a39(2026-10-10),52858aa12e(2026-10-10),5f93ef7f8c(2026-10-10) - Tests:
chord/delta/own_key_order_test.go::TestDiffRevisionsVisitsArrayIndexKeysFirstInNumericOrder,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,coding/extension/host/inproc/ui_working_message_test.go::TestUIPrompt_HiddenThinkingLabelClearerReachesTheBoundUI,coding/extension/host/subprocess/ui_bridge_test.go::TestUIBridge_HandleCall_SetHiddenThinkingLabelDistinguishesAbsentFromEmpty,coding/extension/host/subprocess/ui_bridge_test.go::TestUIBridge_HandleCall_SetWorkingMessageDistinguishesAbsentFromEmpty,coding/extension/host/subprocess/ui_bridge_test.go::TestUIBridge_HandleCall_SetWorkingMessageWithoutAClearerFallsBackToEmpty,coding/extension/ui_test.go::TestNoopUIContext_AllMethodsReturnUpstreamDefaults,extensions/sdk/truncate_test.go::TestFormatSizeRoundsHalfUpLikeToFixedand 15 more in docs/bug-ledger.json - Release note:
setWorkingMessagewith no message restores the default working label and an empty string shows an empty label, as in Pi (message ?? defaultWorkingMessage). Both used to show the default. The Go, Rust and Python extension SDKs gainClearWorkingMessage(),clear_working_message()andset_working_message(None)for the no-argument form;SetWorkingMessage(message)still takes a string. AUIContextcan implement the newextension.WorkingMessageClearerinterface to tell the two apart; one that does not keeps the old behavior, so existing implementations still compile.setHiddenThinkingLabelfollows the same rule (ClearHiddenThinkingLabel(),clear_hidden_thinking_label(),set_hidden_thinking_label(None);extension.HiddenThinkingLabelClearer). A Session replacement now also resets the working message, visibility and indicator an extension set, as Pi does.
BF-53385a3
Registering a large MCP catalog, building a CodeMode sandbox from one, and CodeMode tool discovery were…
- Source: PiG's own defect
- Symptom: Registering a large MCP catalog, building a CodeMode sandbox from one, and CodeMode tool discovery were quadratic in the number of tools, so a 100,000-tool server took tens of seconds to register after its last
tools/listpage,NewSandboxtook 27 seconds, and everysearchTools()ordescribeNamespace()call rescanned the whole host catalog once per tool. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
53385a3d93(2026-10-21, #268) - Tests:
codemode/sandbox_scaling_test.go::TestSandboxConstructionKeepsOrderAndRejectsDuplicates,coding/extension/builtin/codemode/run_test.go::TestDiscoveryDoesNotKeepReadingTheCatalogAfterCancellation,coding/extension/builtin/codemode/run_test.go::TestDiscoveryReadsTheCatalogOnceAndKeepsTheFirstEntryOfAName,coding/extension/builtin/codemode/run_test.go::TestDiscoveryStopsWhenItsContextIsCancelled,coding/mcpext/extension_test.go::TestAgentSessionMCPNamesCollidingToolsAcrossTheWholeCatalog - Release note: Registering a large MCP catalog, building a CodeMode sandbox from one, and CodeMode tool discovery were quadratic in the number of tools, so a 100,000-tool server took tens of seconds to register after its last
tools/listpage,NewSandboxtook 27 seconds, and everysearchTools()ordescribeNamespace()call rescanned the whole host catalog once per tool. Tool names are now assigned from a per-server count of the sanitized names, the sandbox keeps a name index beside its tool list, and a discovery call reads the host catalog once and resolves each tool's namespace by lookup. Names, collision suffixes, registration order, duplicate-tool errors and the first-entry-wins namespace of a repeated name are unchanged. Discovery also now stops when its context is cancelled and returns the context's error; before, it kept scanning. Pi resolves the same way for the sandbox (aMapby name) but scans per tool in the MCP extension and in codemode discovery; PiG's results match Pi's.
BF-5380e0a
fix(extension): load failure shows the extension's own cause; registration edge-case rows
- Source: PiG's own defect
- Symptom: fix(extension): load failure shows the extension's own cause; registration edge-case rows
- Root cause: A failed Go/Rust/Python registration lost its cause line when the host read the stderr log before the copy of the dying process's last lines drained, and the isolated register path never read it. The host now waits (bounded) for the process to exit before reading the cause, in both paths. The Rust panic cause pattern did not match toolchains that print a thread id ('thread 'x' (123) panicked at'), and took the location line instead of the message; it now matches and joins the message. Scenario rows (all four SDKs, loader.ts:289-345): duplicate registrations replace in place and the first flag default wins, a non-object tool schema, a flag default of the wrong type and a nameless command…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
5380e0acc2(2026-10-09) - Tests:
coding/extension/host/subprocess/load_error_cause_test.go::TestStderrCausePicksTheRuntimeErrorLine
BF-54283ce
fix(extension): Shutdown closes live child sessions and releases the objects they hold
- Source: PiG's own defect
- Symptom: fix(extension): Shutdown closes live child sessions and releases the objects they hold
- Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
54283ceb0f(2026-10-21) - Tests: None recorded.
BF-542b536
fix(cli): PIG_VERSION_FORMAT=pi prints the pinned Pi package version
- Source: PiG's own defect
- Symptom: fix(cli): PIG_VERSION_FORMAT=pi prints the pinned Pi package version
- Root cause: Pi's --version prints the package version, which differs from the display version when the pin is a pi-mono commit. Use coding.UpstreamPackageVersion so the opt-in always matches pi --version.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
542b5369f6(2026-10-11) - Tests: None recorded.
BF-5443b6c
durable/images is the image processor of pi-durable: images.CreatePhotonImages() (also images/node and…
- Source: PiG's own defect
- Symptom:
durable/imagesis the image processor of pi-durable:images.CreatePhotonImages()(alsoimages/nodeandimages/cloudflare) is atools.ImageProcessorthat turns an image upright by its EXIF orientation, scales it to the limits with a Lanczos3 filter, converts BMP to PNG, and re-encodes PNG or JPEG at falling qualities until it fits. - Root cause: .upstream/pin names the pinned Pi build: a release tag or a pi-mono commit (version 1.1.0+). pigversion.go is generated from it; coding gains UpstreamRef and UpstreamPackageVersion beside the display version. PIUPSTREAM_REF=<tag|commit> overrides the pin for one run (make variables, a pigversion.go build overlay, PIG_UPSTREAM* in the environment). make pin-bump moves the pin and regenerates what derives from it: SDK, CI-image and Durable package pins, README and ledger version fields, the source mirror, the inventories.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),86fe3dc28b(2026-10-09),f1d3eec6de(2026-10-09),014c014e74(2026-10-10),2de6fe21b8(2026-10-10),30ca7fcf7f(2026-10-10),5443b6c4d5(2026-10-10),691e3e1536(2026-10-10),76d1a85e45(2026-10-10),90b48897e1(2026-10-10),98c1299067(2026-10-10),af1289d2d7(2026-10-10),b7199a2ba1(2026-10-10),c87dea7287(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/builtin_catalog_upstream_test.go::TestBuiltinCatalogsMatchPinnedProviderDataModulesand 72 more in docs/bug-ledger.json - Release note:
durable/imagesis the image processor of pi-durable:images.CreatePhotonImages()(alsoimages/nodeandimages/cloudflare) is atools.ImageProcessorthat turns an image upright by its EXIF orientation, scales it to the limits with a Lanczos3 filter, converts BMP to PNG, and re-encodes PNG or JPEG at falling qualities until it fits.tools.CreateReadTool(&tools.ReadToolOptions{Images: p})andtools.CreateCodingTools(...)pass it toread;tools.CreateReadToolnow takes its options (nil for none), asCreateBashTooldoes.
BF-54a5bf6
The host answers a parsed frontmatter with <, >, & and U+2028 as they are (as JSON.stringify writes…
- Source: PiG's own defect
- Symptom: The host answers a parsed frontmatter with
<,>,&and U+2028 as they are (asJSON.stringifywrites them) instead of\u003c-style escapes, and a packed cell's helper without aContextkeeps a live connection when the first extension to start has stopped (Go, Rust and Python SDKs). - Root cause: The host wrote the frontmatter result with encoding/json, which rewrites < > & and U+2028 as escapes. It now writes the result itself. The helpers' running-connection slot held only the first extension of a packed cell, so a helper with no Context failed when that one stopped: Go, Rust and Python keep every running extension's connection and use the first still running. The parity row is partial, because a value only an explicit YAML tag builds is still an error.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),058e7a0e4b(2026-10-11),15cb4f2db8(2026-10-11),50f526d69e(2026-10-11),54a5bf6140(2026-10-11),8894a13f34(2026-10-11),a178939d6a(2026-10-11),a4e5b071cb(2026-10-11),e4aa58a078(2026-10-11),f51d8b4bea(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/extension/host/subprocess/host_helpers_quote_test.go::TestParseFrontmatterResultKeepsMarkupAndLineSeparatorsLiteral,extensions/sdk-py/tests/test_compaction_estimate.py,extensions/sdk-py/tests/test_helper_running.py,extensions/sdk-py/tests/test_serialize_conversation_surrogates.pyand 13 more in docs/bug-ledger.json - Release note: The host answers a parsed frontmatter with
<,>,&and U+2028 as they are (asJSON.stringifywrites them) instead of\u003c-style escapes, and a packed cell's helper without aContextkeeps a live connection when the first extension to start has stopped (Go, Rust and Python SDKs).
BF-5654e71
MCP OAuth discovery and resource selection now read URLs the way Node's URL does (default ports, dot…
- Source: PiG's own defect
- Symptom: MCP OAuth discovery and resource selection now read URLs the way Node's
URLdoes (default ports, dot segments, backslashes, punycode hosts), andWWW-Authenticatechallenges with quoted or odd values parse as Pi parses them. - Root cause: Pi packages/mcp/src: discovery.ts field() reads an unquoted value with a quote and tests JavaScript's \s and ASCII-only /i; PiG returned an empty value and used RE2's classes. discovery.ts builds URLs with WHATWG new URL (default port, dot segments, backslash, punycode, scheme-relative // paths, origin null for other schemes); PiG used net/url. types.ts keeps empty arrays, reads expires_in with Number() (arrays, 0x10, booleans) before access_token, treats null *_at as absent and spreads unmodeled client members; PiG dropped or rejected each. jsonrpc.ts isJsonRpcRequest/Notification ignore the error member and require a string method.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5654e71185(2026-10-05) - Tests:
mcp/oauth/upstream_oracle_test.go::TestBuildAuthorizationServerDiscoveryURLsMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseAuthorizationServerMetadataMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseClientInformationMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseOAuthTokensMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseProtectedResourceMetadataMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseWWWAuthenticateMatchesPi,mcp/oauth/upstream_oracle_test.go::TestSelectResourceMatchesPi,mcp/oauth/upstream_oracle_test.go::TestStepUpScopeMatchesPiand 1 more in docs/bug-ledger.json - Release note: MCP OAuth discovery and resource selection now read URLs the way Node's
URLdoes (default ports, dot segments, backslashes, punycode hosts), andWWW-Authenticatechallenges with quoted or odd values parse as Pi parses them.
BF-5654e71-2
A JSON-RPC request or notification with a malformed error member is no longer rejected, and a method that…
- Source: PiG's own defect
- Symptom: A JSON-RPC request or notification with a malformed
errormember is no longer rejected, and amethodthat is not a string is. - Root cause: Pi packages/mcp/src: discovery.ts field() reads an unquoted value with a quote and tests JavaScript's \s and ASCII-only /i; PiG returned an empty value and used RE2's classes. discovery.ts builds URLs with WHATWG new URL (default port, dot segments, backslash, punycode, scheme-relative // paths, origin null for other schemes); PiG used net/url. types.ts keeps empty arrays, reads expires_in with Number() (arrays, 0x10, booleans) before access_token, treats null *_at as absent and spreads unmodeled client members; PiG dropped or rejected each. jsonrpc.ts isJsonRpcRequest/Notification ignore the error member and require a string method.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5654e71185(2026-10-05) - Tests:
mcp/oauth/upstream_oracle_test.go::TestBuildAuthorizationServerDiscoveryURLsMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseAuthorizationServerMetadataMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseClientInformationMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseOAuthTokensMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseProtectedResourceMetadataMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseWWWAuthenticateMatchesPi,mcp/oauth/upstream_oracle_test.go::TestSelectResourceMatchesPi,mcp/oauth/upstream_oracle_test.go::TestStepUpScopeMatchesPiand 1 more in docs/bug-ledger.json - Release note: A JSON-RPC request or notification with a malformed
errormember is no longer rejected, and amethodthat is not a string is. A JSON-RPC error response whoseerror.codeorerror.messageisnullis rejected as an invalid message, and one whose code is beyond the float64 range is accepted, as in Pi.
BF-56731e9
/login runs a provider object's OAuth login with Pi's login interaction, so an info event shows in the…
- Source: PiG's own defect
- Symptom: PiG showed the message as a progress line and dropped the links.
- Root cause: It now uses ShowSecretInput.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
56731e90d8(2026-10-09) - Tests:
ai/credential_key_order_test.go::TestAuthStorageKeepsEnvOrderAndFirstPositionOfARepeatedKey,internal/codingagent/device_code_native_test.go::TestProviderOwnedAuthDialogNotifications,internal/codingagent/login_info_event_test.go::TestOAuthInteractionSecretPromptHonorsMaskSecretInput,internal/codingagent/native_provider_login_test.go::TestNativeProviderOAuthLoginSelectPrompt,test/parity/scenarios/oauth/31-provider-object-login-info-links.toml - Release note:
/loginruns a provider object's OAuth login with Pi's login interaction, so aninfoevent shows in the login dialog with its message and links, as Pi shows it. Before, PiG showed the message as a progress line and dropped the links. Asecretprompt in that login now follows the Mask secret input setting, as it does in an API-key login.
BF-56731e9-2
An extension's API-key login no longer shows "Waiting for authentication..." after an authorization URL
- Source: PiG's own defect
- Symptom: An extension's API-key login no longer shows "Waiting for authentication..." after an authorization URL.
- Root cause: It now uses ShowSecretInput.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
56731e90d8(2026-10-09) - Tests:
ai/credential_key_order_test.go::TestAuthStorageKeepsEnvOrderAndFirstPositionOfARepeatedKey,internal/codingagent/device_code_native_test.go::TestProviderOwnedAuthDialogNotifications,internal/codingagent/login_info_event_test.go::TestOAuthInteractionSecretPromptHonorsMaskSecretInput,internal/codingagent/native_provider_login_test.go::TestNativeProviderOAuthLoginSelectPrompt,test/parity/scenarios/oauth/31-provider-object-login-info-links.toml - Release note: An extension's API-key login no longer shows "Waiting for authentication..." after an authorization URL. Pi shows that line only after a device code.
BF-56a0400
Single-line inputs (login prompts, search boxes) ignore xterm modifyOtherKeys text sequences, as Pi's Input…
- Source: PiG's own defect
- Symptom: Single-line inputs (login prompts, search boxes) ignore xterm modifyOtherKeys text sequences, as Pi's Input does; the multi-line editor still accepts them.
- Root cause: Label timestamps are toggled off before any save because a label saved now shows the wall clock.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
56a0400b9c(2026-10-07),f2a2b3409b(2026-10-07) - Tests:
internal/codingagent/tree_row_format_test.go::TestFormatTreeRow_AllEntryTypes,internal/codingagent/tree_selector_oracle_test.go::TestTreeSelectorInputMatchesPi,tui/editor_bindings_oracle_test.go::TestEditorRebindingsMatchPi,tui/input_oracle_test.go::TestInputHandleInputMatchesPi,tui/key_csiu_text_test.go::TestTextInputIgnoresModifyOtherKeysText,tui/scoped_models_list_test.go::TestScopedModels_SearchAcceptsKittyAndPasteInput,tui/select_list_oracle_test.go::TestSelectListInputMatchesPi - Release note: Single-line inputs (login prompts, search boxes) ignore xterm modifyOtherKeys text sequences, as Pi's Input does; the multi-line editor still accepts them.
BF-56f1d3c
A family document member accepts a typed Go value as its seed, as CreateTask input and snapshots already do
- Source: PiG's own defect
- Symptom: A family document member accepts a typed Go value as its seed, as
CreateTaskinput and snapshots already do. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
56f1d3c23a(2026-10-05) - Tests:
durable/examples/pocket_dropin_test.go::TestPocketCommitSubscriberTracksBusySpendAndSubmissions,durable/examples/pocket_dropin_test.go::TestPocketForkInitCopiesAndTrimsDocuments,durable/examples/pocket_dropin_test.go::TestPocketOpenCreatesDocumentsAndSurvivesReopen,durable/examples/pocket_dropin_test.go::TestPocketToolHooksAreIntrospectableAndCallableFromATool,durable/session/watch_doc_typed_test.go::TestDeferUntilPublishedRunsAfterEveryListener,durable/session/watch_doc_typed_test.go::TestFamilyDocSeedAcceptsATypedGoValue - Release note: A family document member accepts a typed Go value as its seed, as
CreateTaskinput and snapshots already do.
BF-56f1d3c-2
Submission.Wait, WaitForTask and the idle waits return after every commit subscriber has run for the…
- Source: PiG's own defect
- Symptom:
Submission.Wait,WaitForTaskand the idle waits return after every commit subscriber has run for the commit that settled them, as Pi's promise continuations do. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
56f1d3c23a(2026-10-05) - Tests:
durable/examples/pocket_dropin_test.go::TestPocketCommitSubscriberTracksBusySpendAndSubmissions,durable/examples/pocket_dropin_test.go::TestPocketForkInitCopiesAndTrimsDocuments,durable/examples/pocket_dropin_test.go::TestPocketOpenCreatesDocumentsAndSurvivesReopen,durable/examples/pocket_dropin_test.go::TestPocketToolHooksAreIntrospectableAndCallableFromATool,durable/session/watch_doc_typed_test.go::TestDeferUntilPublishedRunsAfterEveryListener,durable/session/watch_doc_typed_test.go::TestFamilyDocSeedAcceptsATypedGoValue - Release note:
Submission.Wait,WaitForTaskand the idle waits return after every commit subscriber has run for the commit that settled them, as Pi's promise continuations do.
BF-570f857
Fixed interactive sessions leaving extension prompt sections, such as the MCP mcp_servers list, out of the…
- Source: A fix Pi shipped that PiG ported
- Symptom: Fixed interactive sessions leaving extension prompt sections, such as the MCP
mcp_serverslist, out of the transcript's system message. - Root cause: Interactive mode prepares its own before_agent_start run (prepareRunPrompt) but only the Session's own prompt path stored the run in Session.runPrompt, which promptSectionUpdate reads when the request hook appends the sections patch to the transcript.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
47c300724d(2026-10-07),570f85770f(2026-10-07),b3543f848c(2026-10-07) - Tests:
coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthCancelsASignInWaitingOnTheAuthorizationServerWhenTheSessionShutsDown,coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthClosesTheSessionWithoutRefreshingAnExpiringToken,coding/mcp_oauth_session_upstream_test.go::TestAgentSessionMCPOAuthGivesUpOnPigMcpLoginAfterTimeoutAlsoWhileTheAuthorizationServerHangs,coding/mcpext/manager_test.go::TestMcpManagerReconnectRunsInTheBackgroundWithoutAStatusScreen,coding/mcpext/manager_test.go::TestMcpManagerServerMenuAndDisable,coding/mcpext/manager_test.go::TestMcpManagerStatusScreenCancelsTheRunningOperationWithTheCancelKey,coding/mcpext/manager_test.go::TestMcpManagerStatusScreenIgnoresTheCancelKeyForOperationsThatCannotBeCancelled,coding/mcpext/manager_test.go::TestMcpManagerViewCutsRowsToTheWidthand 23 more in docs/bug-ledger.json - Release note: Fixed interactive sessions leaving extension prompt sections, such as the MCP
mcp_serverslist, out of the transcript's system message. Print mode and Pi record them; interactive mode now publishes its prompt run to the session, which appends the sections before the first request.
BF-5732479
auth.json keeps each credential's properties in the order they were read or returned by an extension's…
- Source: PiG's own defect
- Symptom: PiG rewrote every credential in a fixed key order whenever
auth.jsonwas saved, including credentials of providers that were not changed. - Root cause: BenchmarkCredentialKeyOrderRoundTrip decodes and encodes an auth.json of three credentials. The remaining cost is the default encoding that decides whether an order must be kept. That cost is acceptable, because AuthStorage decodes only when auth.json's revision changes and encodes only on a login, refresh or logout.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),24ee26a82b(2026-10-09),26d302a2d7(2026-10-09, #201),5732479897(2026-10-09),a527291444(2026-10-09),a9956e80d8(2026-10-09),b88044ff8a(2026-10-09),c7ebb23d87(2026-10-09) - Tests:
ai/auth_credential_payload_test.go::TestOAuthCredentialCustomJSONRetainsUTF16Payload,ai/credential_key_order_test.go::TestAuthStorageStoresALoginCredentialInItsOwnKeyOrder,ai/credential_key_order_test.go::TestAuthStorageWritesCredentialsInTheirDecodedKeyOrder,ai/credential_key_order_test.go::TestCredentialFromOAuthPlacesTheDiscriminatorAsPiDoes,ai/credential_key_order_test.go::TestCredentialInCodecOrderCarriesNoKeyOrder,coding/extension/host/subprocess/native_provider_login_test.go::TestDecodeNativeLoginCallback,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeAuthInteractionAnswersTheLoginDialog,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeProviderCarrierCarriesDeclaredLoginsand 15 more in docs/bug-ledger.json - Release note:
auth.jsonkeeps each credential's properties in the order they were read or returned by an extension's login, as Pi keeps the object it stores and writes it withJSON.stringify. Before, PiG rewrote every credential in a fixed key order wheneverauth.jsonwas saved, including credentials of providers that were not changed. Aconfig.oauthlogin's credential gets itstypeafter the flow's own properties, as Pi's{ ...credential, type: "oauth" }does. A credential that one of PiG's built-in logins creates still uses PiG's own key order, not the order of Pi's object literal.
BF-5774749
A Piglet found by name in <project>/.pig/piglets now follows project trust: it is used only when the…
- Source: PiG's own defect
- Symptom: A cloned repository could shadow the user's Piglet of that name, with its system prompt, extensions and model, with no prompt and no flag.
- Root cause: A bare
--piglet <name>is never read as a file in the working directory. - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
da99854d1b(2026-10-09),5774749dbc(2026-10-10),cb0873dbfa(2026-10-10) - Tests:
coding/cli/main_test.go::TestResolvePigletResolvesExtends,coding/cli/main_test.go::TestResolvePiglet_FlagOverridesEnv,coding/cli/main_test.go::TestResolvePiglet_FromEnvPath,coding/cli/main_test.go::TestResolvePiglet_NoFlagNoEnv,coding/cli/mcp_builtin_binary_test.go::TestBuiltinMCPExtensionStartsProjectServersOnlyInATrustedProject,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,coding/piglet/workspace_trust_test.go::TestResolveNamesAnIgnoredUntrustedWorkspacePiglet,coding/piglet/workspace_trust_test.go::TestResolveSkipsAnUntrustedWorkspacePigletAndPrefersTheUsersand 1 more in docs/bug-ledger.json - Release note: A Piglet found by name in
<project>/.pig/pigletsnow follows project trust: it is used only when the project has a stored trust decision of yes or the run passes--approve, and an ignored workspace Piglet is named in the error. A bare--piglet <name>is never read as a file in the working directory. Before, a cloned repository could shadow the user's Piglet of that name, with its system prompt, extensions and model, with no prompt and no flag.
BF-578c1a3
The "Applied use cases" page in the manual that ships inside PiG now links the Security page on the website
- Source: PiG's own defect
- Symptom: It named a
security.mdpage that the shipped manual does not have. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
578c1a3b47(2026-10-11) - Tests: None recorded.
- Release note: The "Applied use cases" page in the manual that ships inside PiG now links the Security page on the website. Before, it named a
security.mdpage that the shipped manual does not have.
BF-579b4d2
fix(durable): a non-finite number is a strict JSON error whichever JSON package a value's own encoder used
- Source: PiG's own defect
- Symptom: fix(durable): a non-finite number is a strict JSON error whichever JSON package a value's own encoder used
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
579b4d240a(2026-10-24) - Tests: None recorded.
BF-57a1687
fix(ai,agent): return a projection's blocks apart from its scalars so the caller's buffer stays on its stack
- Source: PiG's own defect
- Symptom: fix(ai,agent): return a projection's blocks apart from its scalars so the caller's buffer stays on its stack
- Root cause: An earlier change (lw-tui2, merged into int-050 by) made updateAssistantMessageBlock read ObserveProjection into a stack array instead of cloning the message. The projection came back as one struct holding the caller's buffer next to the stop reason and error message. Go's escape analysis does not tell struct fields apart: the block stores the stop reason and error text, so the array moved to the heap, 1 allocation per call.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
57a1687bc4(2026-10-13) - Tests:
agent/messages_projection_test.go::TestAssistantMessageProjectionEqualsObserve,ai/assistant_observation_projection_norace_test.go::TestObserveProjectionAllocatesNothing,ai/assistant_observation_projection_test.go::TestObserveProjectionEqualsObserve,ai/stream_builder_deferred_arguments_test.go::TestDeferredToolArgumentsReadAsTheEagerParse,internal/codingagent/reload_allocs_norace_test.go::TestInteractiveSessionReloadAllocatesPerRetainedMessageWithinBudget
BF-57ee33b
A model an extension registers with registerProvider reaches ctx.modelRegistry, ctx.model and RPC as…
- Source: PiG's own defect
- Symptom: A model an extension registers with
registerProviderreachesctx.modelRegistry,ctx.modeland RPC as Pi's object: the definition's members in the order the extension wrote them, thenapi,providerandbaseUrl, and itsinputas written. - Root cause: The four TestSubprocessModelRegistry* tests required every model an extension reads from ctx.modelRegistry to have promptCache, thinkingLevelMap, samplingParams, headers and compat members. The check was written for the old ModelInfo, which sent null for every absent member; made the objects Pi's and left it behind.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
50ca49180f(2026-10-13),57ee33b5c7(2026-10-13),5cd840e477(2026-10-13) - Tests:
coding/cli/model_registry_refresh_test.go::TestSubprocessModelRegistryPublishesExtensionReplacementAndRestoration,coding/cli/model_registry_refresh_test.go::TestSubprocessModelRegistryPublishesProviderOnlyOverlayRefresh,coding/cli/model_registry_refresh_test.go::TestSubprocessModelRegistryPublishesProviderRegistrationAndRemoval,coding/cli/model_registry_refresh_test.go::TestSubprocessModelRegistryRefreshesConnectedNode,coding/cli/port_wave_10_package_command_paths_test.go::TestPortWave10ModelRuntimeCreation,coding/cli/shipped_piglet_inventory_test.go::TestShippedPigletInventory,coding/cli/strip_leak_gate_test.go::TestStripLeakGate,coding/model_availability_auth_view_test.go::TestAvailabilityPassAuthViewMatchesPiUpstreamand 1 more in docs/bug-ledger.json - Release note: A model an extension registers with
registerProviderreachesctx.modelRegistry,ctx.modeland RPC as Pi's object: the definition's members in the order the extension wrote them, thenapi,providerandbaseUrl, and itsinputas written. PiG sent the members in its own order and filled ininput: ["text"]when the definition left it out.
BF-59456ae
Pressing a key in a subprocess extension's overlay no longer freezes interactive mode when the key reaches…
- Source: PiG's own defect
- Symptom: Pressing a key in a subprocess extension's overlay no longer freezes interactive mode when the key reaches the overlay on the main loop (public PR #197).
- Root cause: The subprocess bridge reads a focused overlay's state before it forwards a key. That read queued a task on the owner loop and waited for its result, so a key delivered on the owner loop waited for work only that loop could run. Pi reads the same state synchronously on its single event loop. The owner loop is now identified by goroutine: a caller already on it runs owner work inline, and a post to its own full queue no longer blocks.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
59456ae205(2026-10-09) - Tests:
internal/codingagent/overlay_owner_loop_deadlock_test.go::TestNestedOwnerLoopRestoresOwner,internal/codingagent/overlay_owner_loop_deadlock_test.go::TestOwnerLoopIdentity,internal/codingagent/overlay_owner_loop_deadlock_test.go::TestPostToMainWithRoomSkipsOwnerCheck,internal/codingagent/overlay_owner_loop_deadlock_test.go::TestRemoteOverlayKeyOnOwnerLoopDoesNotWaitOnOwnerQueue,internal/codingagent/overlay_owner_loop_deadlock_test.go::TestRunOnMainAndWaitOnOwnerLoopRunsInline,internal/codingagent/overlay_owner_loop_deadlock_test.go::TestRunOnMainFromOwnerLoopNeverBlocksOnItsOwnQueue,internal/codingagent/overlay_owner_loop_deadlock_test.go::TestRunOnOwnerInlineOnLoopQueuedElsewhere - Release note: Pressing a key in a subprocess extension's overlay no longer freezes interactive mode when the key reaches the overlay on the main loop (public PR #197).
BF-598e27f
Escape over an open autocomplete list during compaction now closes the list and lets compaction continue, as…
- Source: PiG's own defect
- Symptom: Previously it aborted the compaction and left the list open.
- Root cause: CustomEditor runs onEscape only while no autocomplete is showing (custom-editor.ts:106-116); otherwise super.handleInput closes the list. Pig's CustomEditor falls through to superInput, which re-enters handleEditorAction with actionInterrupt, and the compaction-abort check (Pi's compaction_start onEscape, interactive-mode.ts:3678) ran before the autocomplete intercept: Escape aborted the compaction and left the list open. The check now follows the intercept. TestInteractiveMode_EscapeClosesAutocompleteDuringCompactionWithoutAborting is red on the old order.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
598e27f7e9(2026-10-10) - Tests:
internal/codingagent/custom_editor_test.go::TestCustomEditorActionLoopSkipsInterruptAndExit,internal/codingagent/custom_editor_test.go::TestCustomEditorEscapeOverAutocompleteGoesToTheEditor,internal/codingagent/interactive_test.go::TestInteractiveMode_EscapeClosesAutocompleteDuringCompactionWithoutAborting - Release note: Escape over an open autocomplete list during compaction now closes the list and lets compaction continue, as in Pi. Previously it aborted the compaction and left the list open.
BF-59fb623
fix(requestvalidity): one Responses assistant turn holds all its items, in any order
- Source: PiG's own defect
- Symptom: fix(requestvalidity): one Responses assistant turn holds all its items, in any order
- Root cause: The Responses projection opened a new assistant turn for a message or reasoning item that followed a function_call, so an assistant message with text after its tool call ([toolCall, text], which a model may stream) was reported as between-call-and-result plus result-has-call. OpenAI documents only a missing output ("No tool output found for function call") or a missing call as errors. In a test binary the checker ends the process on that request, so any test replaying such a history through openai-responses, azure-openai-responses or openai-codex-responses died. Input items carry no assistant-message boundary, so consecutive assistant items now form one turn. A user, system or developer…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
59fb6231c2(2026-10-09) - Tests:
ai/request_validity_test.go::TestProviderSendSeamChecksEveryAPIsRequest,internal/requestvalidity/requestvalidity_test.go::TestCheckBodyContracts,test/pipeline-diff/diff_test.go::TestExplore,test/pipeline-diff/diff_test.go::TestShow
BF-5a093cd
In print and JSON mode, Node extensions that share one process now keep Pi's order at disposal
- Source: PiG's own defect
- Symptom: An earlier extension's queued callback could run before a later extension's
session_shutdownhandler started, or a timer could run with a live ctx after the runner was invalidated, depending on the order in which the process read its sockets. - Root cause: Pi's runner awaits each extension's handler in turn on one event loop (runner.ts:1089-1116). When a later extension's session_shutdown or agent_settled handler awaits a timer or I/O, that loop runs the immediate or timer an earlier extension's handler queued, with a live ctx, before print mode invalidates the runner (agent-session-runtime.ts:404-410, agent-session.ts:1393-1406). With isolated Node processes the earlier process waited for the host's next step after answering (), so its callbacks ran only after invalidate, or never, because the host stopped the process first. Packed processes share one loop and matched Pi.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
49798a80d2(2026-10-07),1f7fa2fe22(2026-10-09),5a093cd8f6(2026-10-09) - Tests:
coding/extension/host/subprocess/loop_turned_forward_test.go::TestLoopTurnedReachesOnlyOtherNodeProcesses,coding/extension/host/subprocess/runtime_node_loop_turn_test.go::TestNodeLoopTurnRunsQueuedImmediatesBeforeLaterFrames,coding/extension/host/subprocess/runtime_node_loop_turn_test.go::TestNodePromptEndWaitIgnoresAFinishedRunsSignal,coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodePackedPromptEndWaitHoldsTimersUntilEveryMemberIsInvalidated,coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodePromptEndWaitEndsWhenAnotherProcessTurnsTheLoop,extensions/sdk/surface_parity_test.go::TestLoopTurnedNotifyChangesNothing,test/parity/scenarios/extensions-runtime/75-print-shutdown-loop-turn-isolated.toml,test/parity/scenarios/extensions-runtime/75-print-shutdown-loop-turn.toml - Release note: In print and JSON mode, Node extensions that share one process now keep Pi's order at disposal. Before, an earlier extension's queued callback could run before a later extension's
session_shutdownhandler started, or a timer could run with a live ctx after the runner was invalidated, depending on the order in which the process read its sockets.
BF-5aab94c
The color level check reads TERM with JavaScript's case-insensitive matching, as Pi's chalk does: a name…
- Source: PiG's own defect
- Symptom: The color level check reads
TERMwith JavaScript's case-insensitive matching, as Pi's chalk does: a name such asſcreen(long s) no longer counts asscreen. - Root cause:
- ai/validate_formats.go: formatDateTimeSeparator. - coding/cli/extension_upgrade.go: backupNameUnsafe. - coding/extension/source/resolve.go: pythonModuleName. - internal/packagemanager/package_updates.go: lsRemoteHash and lsRemoteHEAD. - internal/ignorerules/ignorerules.go: a shared matchEverything (a *regexp.Regexp is safe for concurrent use). Each is hoisted to a package-level lazyregexp (or a plain regexp for the empty pattern); the patterns are unchanged.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5aab94c69d(2026-10-10) - Tests:
coding/cli/chalk_color_oracle_test.go::TestCLIChalkColorLevelMatchesChalk - Release note: The color level check reads
TERMwith JavaScript's case-insensitive matching, as Pi's chalk does: a name such asſcreen(long s) no longer counts asscreen. Ten regular expressions that were compiled on every call are now compiled once.
BF-5ab2304
Starting an extension no longer fails with fork/exec …: text file busy when the host builds or starts…
- Source: PiG's own defect
- Symptom: Starting an extension no longer fails with
fork/exec …: text file busywhen the host builds or starts other extensions at the same moment (seen as aci-test-conformancefailure inTestModelTypesVirtualModelsAcrossSDKs/rust-isolated). - Root cause: the host copies a compiled binary into the cache with an in-process write (copyArtifact for a cell runner when CARGO_TARGET_DIR puts the compiler output outside the build scratch; copyBuiltBinary, formerly inline in buildRust, for a standalone Rust extension; atomicWriteFile for an embedded cell of a Piglet Binary) and executes the copy as soon as it is published. A child that another goroutine forks while the copy is open holds a copy of the write descriptor until it calls exec. Close-on-exec closes it at exec, not at fork, so the host's exec of the binary fails while that child is still between fork and exec. Renaming the file into place does not help: ETXTBSY follows the open file, not…
- User impact: An operation failed or was rejected
- Platform: Linux
- Fixed in: Unreleased
- Fix commits:
5ab230421c(2026-10-24) - Tests:
coding/extension/host/cellpack/extract_fork_test.go::TestAtomicWriteFileStartsNoChildWhileWriting,coding/extension/host/runtimecell/cellcache_exec_test.go::TestPublishArtifactStartsNoChildWhileCopyingAnExternalArtifact,coding/extension/host/subprocess/builder_rust_copy_test.go::TestCopyBuiltBinaryStartsNoChildWhileCopying,internal/execfile/execfile_test.go::TestWriteReleasesTheForkLockWhenItFails,internal/execfile/execfile_test.go::TestWriteStartsNoChildWhileTheFileIsOpen,internal/execfile/execfile_test.go::TestWriteTruncatesWhenAsked,internal/execfile/execfile_unix_test.go::TestStartAfterWriteIsNeverBusy,internal/execfile/execfile_unix_test.go::TestWriteLeavesNoDescriptorOpenand 1 more in docs/bug-ledger.json - Release note: Starting an extension no longer fails with
fork/exec …: text file busywhen the host builds or starts other extensions at the same moment (seen as aci-test-conformancefailure inTestModelTypesVirtualModelsAcrossSDKs/rust-isolated). The host copies a compiled extension binary into its cache and starts the copy as soon as it is published: a Rust cell runner whenCARGO_TARGET_DIRputs the compiler output outside the build directory, a standalone Rust extension, and the embedded cells of a Piglet Binary. A process that another goroutine started while the copy was open still held the copy's write descriptor, and Linux refuses to start a file that any process holds open for writing. The host now starts no process from before the copy is opened until after it is closed, so a start waits for at most one 256 KiB chunk of a copy (about 0.2 ms), not the whole copy. A standalone Rust extension build also no longer reads the whole binary into memory to copy it.
BF-5b0e909
/scoped-models: an empty search no longer makes enable-all/clear-all act on every model, clearing every model…
- Source: PiG's own defect
- Symptom: /scoped-models: an empty search no longer makes enable-all/clear-all act on every model, clearing every model from an all-enabled list now leaves none enabled (not all), and the save key is checked after the navigation keys like Pi.
- Root cause: (2) clearing every model from an all-enabled list produced a nil slice, i.e. 'all enabled', instead of an empty explicit list. (3) the save action was checked before navigation; Pi checks up, down, reorder, confirm, enableAll, clearAll, toggleProvider, save, ctrl+c, escape, so a shared key keeps the earlier action. Found, not changed: both Pi and Pig crash (TypeError / index out of range [-1]) when a reorder with an active search moves the selection above the first filtered row (reorder bounds are checked against the enabled list, not the filtered one).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
56a0400b9c(2026-10-07),5b0e90985b(2026-10-07) - Tests:
internal/codingagent/scoped_models_oracle_test.go::TestScopedModelsSelectorMatchesPi,tui/editor_bindings_oracle_test.go::TestEditorRebindingsMatchPi,tui/input_oracle_test.go::TestInputHandleInputMatchesPi,tui/key_csiu_text_test.go::TestTextInputIgnoresModifyOtherKeysText,tui/scoped_models_list_test.go::TestScopedModels_SearchAcceptsKittyAndPasteInput,tui/select_list_oracle_test.go::TestSelectListInputMatchesPi - Release note: /scoped-models: an empty search no longer makes enable-all/clear-all act on every model, clearing every model from an all-enabled list now leaves none enabled (not all), and the save key is checked after the navigation keys like Pi.
BF-5b15407
In-process extensions that call pi.getCommands() now receive the extension commands, prompt templates and…
- Source: PiG's own defect
- Symptom: In-process extensions that call
pi.getCommands()now receive the extension commands, prompt templates and skills of the session in every mode, as Pi's session binds them. - Root cause: Pi's PackageManager/DefaultPackageManager (package-manager.ts) was a set of cmd/pig helper functions taking (cwd, settings). They move to internal/packagemanager as PackageManager with setProgressCallback, listConfiguredPackages, getInstalledPath, install, installAndPersist, remove, removeAndPersist, addSourceToSettings and removeSourceFromSettings; cmd/pig builds one per command with packageManagerFor and its ~90 call sites (production and tests) use the methods. Behaviour is unchanged (cmd/pig package/install/remove/settings/npm/git tests pass).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8d990c5d54(2026-10-07),a79de03e7f(2026-10-07),fe4a0bf7a4(2026-10-07),5b15407a4b(2026-10-08) - Tests:
coding/cli/session_extension_actions_listing_test.go::TestSessionSlashCommandsPlaceResourcesWithoutSourceInfoByTheSessionDirectories,coding/extension/host/inproc/upstream_scoped_models_test.go::TestContextThinkingLevelReadsTheBoundRuntimeLive,internal/codingagent/docs_path_test.go::TestGetDocsPathIsAbsoluteForARelativeConfigRoot,internal/codingagent/docs_path_test.go::TestPromptDocsSectionNamesGetDocsPathWithATildeConfigRoot,internal/codingagent/interactive_get_commands_test.go::TestInteractiveWiringBindsGetCommandsToTheRuntime,internal/codingagent/tools/bash_spawn_hook_test.go::TestBashSpawnHookAdjustsCommandCwdAndEnv,internal/codingagent/tools/powershell_operations_test.go::TestPowerShellToolCallsCustomOperations,internal/configroot/configroot_test.go::TestDirPrefersPigHomeThenXDGThenTheHomeDirectoryand 4 more in docs/bug-ledger.json - Release note: In-process extensions that call
pi.getCommands()now receive the extension commands, prompt templates and skills of the session in every mode, as Pi's session binds them.
BF-5b1d5b1
A resource pattern that starts with ~ (!~/claude-backup/skills/pdf, +~/…, -~/…, wildcards) now…
- Source: PiG's own defect
- Symptom: A resource pattern that starts with
~(!~/claude-backup/skills/pdf,+~/…,-~/…, wildcards) now expands the home directory like a plain entry instead of silently matching nothing, and afile://URL pattern is its path, as Pi 1.2.0 does (pi-mono #10765). - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5b1d5b1382(2026-10-10) - Tests: None recorded.
- Release note: A resource pattern that starts with
~(!~/claude-backup/skills/pdf,+~/…,-~/…, wildcards) now expands the home directory like a plain entry instead of silently matching nothing, and afile://URL pattern is its path, as Pi 1.2.0 does (pi-mono #10765).
BF-5b4a3d3
A lazy line no longer continues a quote whose last quoted line is empty (>\nnext is an empty quote then a…
- Source: PiG's own defect
- Symptom: A lazy line no longer continues a quote whose last quoted line is empty (
>\nnextis an empty quote then a paragraph), and a quote that renders no rows still adds its spacing row. - Root cause: marked's blockquote tokenizer joins a lazy line only when the quoted line before it is not empty (a line of spaces counts); an empty quote is still a token whose spacing row is not merged with a blank. 10 new rows in TestMarkdownRenderMatchesPi (5 red before). Known gap: '> a\n> \nb' (the lazy line joins the paragraph with the spaces line kept).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5b4a3d393e(2026-10-10),cdf0be76e4(2026-10-10) - Tests:
tui/markdown_render_oracle_test.go::TestMarkdownRenderMatchesPi - Release note: A lazy line no longer continues a quote whose last quoted line is empty (
>\nnextis an empty quote then a paragraph), and a quote that renders no rows still adds its spacing row.
BF-5bc4773
RPC set_model applies after its auth check, as Pi's awaited setModel does, so a cycle_model in the same…
- Source: PiG's own defect
- Symptom: RPC
set_modelapplies after its auth check, as Pi's awaitedsetModeldoes, so acycle_modelin the same input batch cycles from the previous model and answers first. - Root cause: Also pins sdk.ts:224-231 in the existing restore test (row 19 gate needs the provider's auth).
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2df65dffcc(2026-10-09),5bc4773278(2026-10-09) - Tests:
coding/cli/rpc_set_model_await_test.go::TestRPCSetModelAppliesAfterTheBatchesLaterCommands,coding/session_context_settings_upstream_test.go::TestRestoreSessionRuntimeStateExplicitModelSelection,coding/session_resume_auth_gate_test.go::TestResumedSessionDoesNotRestoreAModelWithoutAuth - Release note: RPC
set_modelapplies after its auth check, as Pi's awaitedsetModeldoes, so acycle_modelin the same input batch cycles from the previous model and answers first. A session resumed without credentials no longer restores its saved model: it starts on the unknown placeholder, as Pi does.
BF-5bdbf20
fix(cli): keep scratch install output off the validate-only report
- Source: PiG's own defect
- Symptom: fix(cli): keep scratch install output off the validate-only report
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
5bdbf201de(2026-10-22) - Tests: None recorded.
BF-5c4167a
fix(extension): hold the theme colors map the wire palette memo is keyed on
- Source: PiG's own defect
- Symptom: fix(extension): hold the theme colors map the wire palette memo is keyed on
- Root cause: ActiveExtensionThemeWire identified the terminal-resolved colors by the address of the map theme.Colors returned, stored as a uintptr. The theme drops that map when the terminal's reported colors change, so the collector could free it and give its address to a later colors map with other values; the memo would then return the old palette and the state push would skip the theme as unchanged. The memo now holds the map it is keyed on, so the address cannot be reused while it identifies the palette.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
5c4167aad9(2026-10-11) - Tests:
internal/codingagent/ext_ui_theme_palette_test.go::TestActiveExtensionThemeWireHoldsTheColorsMapItIsKeyedOn
BF-5c9d23f
A provider object's OAuth login receives Pi's login options, as oauth.login(interaction, options) does in Pi
- Source: PiG's own defect
- Symptom: A provider object's flow got no options, so a flow that needs the device ID could not get it.
- Root cause: Python: pig_sdk.LoginOptions(get_device_id, agent_name), with the same dispatch and proxy behavior. Only oauth.login receives it, because Pi declares apiKey.login with the interaction alone (types.ts:175).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
95d51d5fa0(2026-10-09),da99854d1b(2026-10-09),f7e3843ab1(2026-10-09),5c9d23fa6c(2026-10-10) - Tests:
ai/oauth_credential_members_test.go::TestOAuthBuildersWriteCredentialMembersInPiOrder,ai/oauth_credential_members_test.go::TestRefreshFirstOAuthBuildersWriteCredentialMembersInPiOrder,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeAuthInteractionAnswersTheLoginDialog,coding/extension/host/subprocess/native_provider_oauth_test.go::TestNativeProviderOAuthLoginCallbacks,coding/extension/host/subprocess/native_provider_oauth_test.go::TestNativeProviderOAuthLoginGetsTheDeviceID,test/extension-conformance/provider_object_test.go::TestNodeRemoteProviderObjectCarrier,test/extension-conformance/provider_object_test.go::TestProviderObjectsAcrossSDKsand 1 more in docs/bug-ledger.json - Release note: A provider object's OAuth login receives Pi's login options, as
oauth.login(interaction, options)does in Pi.options.getDeviceId()returns the app's device ID, and the app creates that ID only when a flow first calls it.options.agentNameis passed when the app sets it. Before, a provider object's flow got no options, so a flow that needs the device ID could not get it.
BF-5d004ae
fix: give Windows git children core.longpaths through the environment, keeping Pi's argv
- Source: PiG's own defect
- Symptom: fix: give Windows git children core.longpaths through the environment, keeping Pi's argv
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: Unreleased
- Fix commits:
5d004aef80(2026-10-22) - Tests:
coding/cli/git_checkout_path_test.go::TestGitLongPathsEnvPerPlatform
BF-5d36a58
fix(agent): end the tool flow at custom user messages and hold system messages mid-flow
- Source: PiG's own defect
- Symptom: fix(agent): end the tool flow at custom user messages and hold system messages mid-flow
- Root cause: Pi converts messages to LLM form and then runs transformMessages (packages/ai/src/api/transform-messages.ts), where a branchSummary, compactionSummary, bashExecution or custom message is already a user message and closes the pending tool calls (closePendingToolCalls). PiG normalizes AgentMessages before convertToLLM, so those custom messages did not interrupt the flow and the placeholders landed behind them. NormalizeMessages now treats a custom message that convertToLLM sends as a user message as a user turn, and holds a system message that sits between a tool call and its results until after the results (transform-messages.ts heldSystemMessages).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
5d36a5821d(2026-10-09) - Tests:
agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,coding/session_tree_wire_oracle_test.go::TestNavigateTreeEveryPointSendsValidToolFlow
BF-5d6bb25
The request-string walk that splices cached strings into a request body finds a cycle by the pointer, map or…
- Source: PiG's own defect
- Symptom: The request-string walk that splices cached strings into a request body finds a cycle by the pointer, map or slice it is inside instead of stopping at a depth of 1,000: a payload that holds itself still fails as the marshaler fails, and a payload nested deeper than 1,000 levels has its large strings spliced like any other (the body is the same bytes).
- Root cause: The map and slice cycles report the marshaler's error.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5d6bb25cdc(2026-10-10),6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/request_string_splice_test.go::TestMarshalSplicedWalksAnyDepthAndReportsEveryKindOfCycle,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,test/longsession/session_oracle_test.go::TestSessionManagerMatchesPiand 1 more in docs/bug-ledger.json - Release note: The request-string walk that splices cached strings into a request body finds a cycle by the pointer, map or slice it is inside instead of stopping at a depth of 1,000: a payload that holds itself still fails as the marshaler fails, and a payload nested deeper than 1,000 levels has its large strings spliced like any other (the body is the same bytes).
BF-5d8eda1
pig auth check and pig auth print-api-key no longer reject --api-key that another option takes as its…
- Source: PiG's own defect
- Symptom:
pig auth checkandpig auth print-api-keyno longer reject--api-keythat another option takes as its value (--model --api-key,--provider --api-key): only an--api-keythat is itself a flag counts, as in Pi. - Root cause: ValidateAuthCommandArgs decided that --api-key was given by scanning the raw arguments for the token, so pig auth check --model --api-key --provider --model (--model takes --api-key as its value) failed with "Auth commands only accept --provider and --model". Pi tests args.apiKey !== undefined, which only a parsed --api-key sets. parseArgs records that (apiKeySet, also for an empty value) and the validator reads it; the raw-argument parameter is no longer read. Found by seed sweeps K=6 and K=7 of TestAuthCommandMatchesPi.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),5d8eda1fb2(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/cli/args_upstream_test.go::TestArgsUpstream,coding/cli/auth_command_oracle_test.go::TestAuthCommandMatchesPi,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgainand 2 more in docs/bug-ledger.json - Release note:
pig auth checkandpig auth print-api-keyno longer reject--api-keythat another option takes as its value (--model --api-key,--provider --api-key): only an--api-keythat is itself a flag counts, as in Pi.
BF-5dbfed1
A scripted faux response can carry DurationMs
- Source: PiG's own defect
- Symptom: A scripted faux response can carry
DurationMs. - Root cause: It closes ClientOptions.transportFactory, ByteTransportFactory and ByteTransport.send (gaps 403 -> 395). The callback is the Promise settlement the client keeps (internal/experimental/client/transport.go:27, connection.go:170).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5dbfed1576(2026-10-08) - Tests:
ai/faux_duration_test.go::TestFauxScriptedDurationMsReachesEveryMessage,ai/faux_scripted_fields_oracle_test.go::TestFauxScriptedMessageMembersMatchPi - Release note: A scripted faux response can carry
DurationMs. It stays on every partial and the final message, as in Pi, and the stream measures a duration only when none is scripted.
BF-5e36d87
The assistant-message frame encoder no longer re-parses and re-serializes a whole tool call's arguments for…
- Source: PiG's own defect
- Symptom: The assistant-message frame encoder no longer re-parses and re-serializes a whole tool call's arguments for every delta while it catches up to the start snapshot (8,000 deltas allocated 19 GB; they now allocate 12 MB).
- Root cause: Catch-up re-parsed and re-serialized the whole argument text per delta (19 GB for 8,000 deltas). Use the incremental streamingArgumentsParser, cache the parsed snapshot, and skip serialization while a top-level string of the snapshot is longer than the same member so far (neither equal nor a prefix).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5e36d8772b(2026-10-09) - Tests: None recorded.
- Release note: The assistant-message frame encoder no longer re-parses and re-serializes a whole tool call's arguments for every delta while it catches up to the start snapshot (8,000 deltas allocated 19 GB; they now allocate 12 MB). The frames it emits are unchanged.
BF-5e3e346
An explicit anthropic-beta header, from a model's headers or the request, no longer makes a…
- Source: A Pi defect that PiG fixed first (divergence D130)
- Symptom: An explicit
anthropic-betaheader, from a model'sheadersor the request, no longer makes a managed-effort Claude model (claude-opus-5, opus-5-5, sonnet-5-5, haiku-5-5, fable-5-1) fail with 400. - Root cause: buildParams now computes the betas before converting the messages. - The markers, including the historical per-turn ones, go only with the mid-conversation beta. - applySampling sends block_binding only with the binding beta. - Without the markers, the active effort is the top-level output_config effort instead of "high". Without an override, or with one listing both betas, the request is Pi's. tool_addition blocks without inline-tools-2026-09-15 are left as Pi has them; the issue did not test that case.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10759
- Removed when Pi fixes it: Pi derives
block_bindingand the mid-conversation markers from the betas it sends. - Fix commits:
5e3e346a69(2026-10-10) - Tests:
ai/anthropic_beta_override_test.go::TestAnthropicManagedEffortBodyFollowsTheBetasSent - Release note: An explicit
anthropic-betaheader, from a model'sheadersor the request, no longer makes a managed-effort Claude model (claude-opus-5, opus-5-5, sonnet-5-5, haiku-5-5, fable-5-1) fail with 400. The override still replaces the default betas. PiG now sendsthinking.block_bindingonly withthinking-binding-controls-2026-08-01, and the mid-conversation effort markers only withmid-conversation-output-config-2026-07-01. Without the markers, the effort is sent as the top-level effort. Pi sends both regardless (Pi #10759, D130).
BF-5eab79d
processImage against live Pi: new TestProcessImageMatchesPi…
- Source: PiG's own defect
- Symptom:
processImageagainst live Pi: newTestProcessImageMatchesPi(internal/imageprocessing/process_image_oracle_test.go; stacked on r16 because it needs the zero-side fix; 400 seeded inputs: PNG/JPEG/GIF/BMP/TIFF/WebP, garbage and empty bytes, declared types with parameters/case/spaces, unsupported and mismatched types, resizing on/off with small limits; ok, mime type, ordered hints and omission message compared). - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),6a6fffa638(2026-10-10),3d475c443d(2026-10-11),5eab79d677(2026-10-11) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResultand 21 more in docs/bug-ledger.json - Release note:
processImageagainst live Pi: newTestProcessImageMatchesPi(internal/imageprocessing/process_image_oracle_test.go; stacked on r16 because it needs the zero-side fix; 400 seeded inputs: PNG/JPEG/GIF/BMP/TIFF/WebP, garbage and empty bytes, declared types with parameters/case/spaces, unsupported and mismatched types, resizing on/off with small limits; ok, mime type, ordered hints and omission message compared). One real fix, red first (9 of 400, 8 of them this):ResizeImageexcluded BMP bytes from the "already within every limit" shortcut (&& srcFormat != "bmp", undocumented, from the first release), so BMP bytes declared as a supported type (png/jpeg/gif/webp) were re-encoded to PNG with a dimension note where Pi returns the bytes unchanged under the declared type; the clause is gone (a BMP declaredimage/bmpis converted to PNG before resizing, in both). 9 mutations red; 1 equivalent (from == toin the conversion hint: an unsupported declared type never equals the PNG/JPEG result). Not compared: the returned bytes.
BF-5ec5f5f
The built-in bash, read and list tool cards now show an image block that has no MIME type as `[Image…
- Source: PiG's own defect
- Symptom: The built-in bash, read and list tool cards now show an image block that has no MIME type as
[Image: [image/unknown]], as Pi'sgetTextOutputdoes. - Root cause: toolResultTextOutput showed an image block without a MIME type as [Image: []]; it now delegates to tools.GetTextOutput (render-utils.ts:54). The clipboard-image.ts mutants no longer edit interactive-mode.ts code (handleClipboardPaste), and the clipboard.ts mutants no longer edit clipboard-command.ts behavior.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5ec5f5f282(2026-10-09) - Tests:
internal/codingagent/render_utils_pi_test.go::TestRenderUtilsReplaceTabsAndTextOutputMatchPi - Release note: The built-in bash, read and list tool cards now show an image block that has no MIME type as
[Image: [image/unknown]], as Pi'sgetTextOutputdoes. They previously showed[Image: []].
BF-5f03fa4
An extension that a child agent session's resource loader loads now receives the session's events, as in Pi…
- Source: PiG's own defect
- Symptom: An extension that a child agent session's resource loader loads now receives the session's events, as in Pi, where
createAgentSessionover aDefaultResourceLoaderthat loads an extension gives itsession_start(afterbindExtensions),input,before_agent_start,agent_start,turn_*,message_*,contextandagent_end. - Root cause: A DefaultResourceLoader loaded its extensions under the context of the reload that asked for them. A child agent session is created, and its loader reloaded, inside the wire call of createAgentSession, so the host stopped every extension the loader had loaded when that call returned: the extension had registered its tools and then received no event. The loader now owns the extensions it loaded until its next load or Close, as Pi's reload() has no caller whose end stops them; the context of the reload still ends the load itself.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
5f03fa4a4e(2026-10-21),78c1d22259(2026-10-21),d8da9b372a(2026-10-21) - Tests: None recorded.
- Release note: An extension that a child agent session's resource loader loads now receives the session's events, as in Pi, where
createAgentSessionover aDefaultResourceLoaderthat loads an extension gives itsession_start(afterbindExtensions),input,before_agent_start,agent_start,turn_*,message_*,contextandagent_end. In the Go, Python and Rust SDKs (through theresourcesoption or aresourceLoaderhandle) the extension registered its tools and then received nothing: the loader started its extensions under the context of the call that reloaded it, which ends whencreateAgentSessionreturns, so the host stopped every extension it had just loaded and reported no error. The loader now owns its extensions until its next load orClose; the context of a reload still ends the reload itself.
BF-5f03fa4-2
The contexts of those extensions read the child session: ctx.model, ctx.scopedModels (the session's…
- Source: PiG's own defect
- Symptom: The contexts of those extensions read the child session:
ctx.model,ctx.scopedModels(the session'sscopedModels, which the child wire accepted and no extension saw),ctx.isIdle(),ctx.hasUI(false) and the headless dialogs (notify,select,inputandeditoranswer nothing,confirmanswers false), as the context of a Pi child session has. - Root cause: A DefaultResourceLoader loaded its extensions under the context of the reload that asked for them. A child agent session is created, and its loader reloaded, inside the wire call of createAgentSession, so the host stopped every extension the loader had loaded when that call returned: the extension had registered its tools and then received no event. The loader now owns the extensions it loaded until its next load or Close, as Pi's reload() has no caller whose end stops them; the context of the reload still ends the load itself.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
5f03fa4a4e(2026-10-21),d8da9b372a(2026-10-21) - Tests: None recorded.
- Release note: The contexts of those extensions read the child session:
ctx.model,ctx.scopedModels(the session'sscopedModels, which the child wire accepted and no extension saw),ctx.isIdle(),ctx.hasUI(false) and the headless dialogs (notify,select,inputandeditoranswer nothing,confirmanswers false), as the context of a Pi child session has. The loader's extension host has a headless UI bridge for this, and a child session binds the state print mode binds to it.
BF-5f22b85
fix(coding): shut down the extension host of a DefaultResourceLoader an extension constructed when its last…
- Source: PiG's own defect
- Symptom: fix(coding): shut down the extension host of a DefaultResourceLoader an extension constructed when its last holder lets go
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
5f22b85fa3(2026-10-21) - Tests: None recorded.
BF-60c4c6f
A tool call whose finished arguments are not a valid JSON object no longer runs with the parsed prefix: its…
- Source: PiG's own defect
- Symptom: A tool call whose finished arguments are not a valid JSON object no longer runs with the parsed prefix: its arguments are
{}andargumentsErrorholds the reason and the text the model sent, which tool validation reports back to the model. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
60c4c6f887(2026-10-14, #10944) - Tests:
ai/json_parse_upstream_test.go::TestParseToolCallArguments,ai/openai_responses_partial_json_cleanup_upstream_test.go::TestOpenAIResponsesMarksMalformedFinalArguments,ai/rpc33_provider_abort_test.go::TestOpenAIProviderAbortAfterAdmission,ai/rpc33_provider_abort_test.go::TestOpenAIProviderAbortAtParserReadBoundaries,ai/rpc33_provider_scratch_test.go::TestProviderScratchInterleavedTools,ai/stream_builder_deferred_arguments_test.go::TestDeferredToolArgumentsReadAsTheEagerParse,ai/validate_upstream_test.go::TestValidateToolArgumentsReportsArgumentsError - Release note: A tool call whose finished arguments are not a valid JSON object no longer runs with the parsed prefix: its arguments are
{}andargumentsErrorholds the reason and the text the model sent, which tool validation reports back to the model. Carried forward from Pi 87f9a364 (earendil-works/pi#10944, #10897), past the 1.2.0+3daa2028 pin.
BF-612955a
The Rust and Go SDKs keep an unpaired surrogate through prepareBranchEntries…
- Source: PiG's own defect
- Symptom: The Rust and Go SDKs keep an unpaired surrogate through
prepareBranchEntries(prepare_branch_entries_json,PrepareBranchEntriesJSON,Context::get_entries_json), and the token estimate of all three SDKs counts it asJSON.stringifydoes in a tool call's arguments. - Root cause: A 6,000-message differential against Pi's own serializeConversation (node + dist) now has 0 differences in Go and Python. Before the fix it had 53 in Go and 33 in Python. The conformance row cannot see this, because the harness decodes reports with encoding/json, which turns every lone surrogate into U+FFFD.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),058e7a0e4b(2026-10-11),25bd4f0080(2026-10-11),2699291af5(2026-10-11),612955add9(2026-10-11),891834837e(2026-10-11),a178939d6a(2026-10-11),a4e5b071cb(2026-10-11),be2cfbd1f5(2026-10-11),e4aa58a078(2026-10-11),e8473d13c5(2026-10-11),f51d8b4bea(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/cli/extension_host_wiring_binary_test.go::TestHeadlessExtensionSessionActionsMatchPi,extensions/sdk-py/tests/test_compaction_estimate.py,extensions/sdk-py/tests/test_serialize_conversation_surrogates.py,extensions/sdk/compaction_estimate_test.go::TestEstimateTokensCountsUTF16UnitsAndJSONStringifyLengthand 11 more in docs/bug-ledger.json - Release note: The Rust and Go SDKs keep an unpaired surrogate through
prepareBranchEntries(prepare_branch_entries_json,PrepareBranchEntriesJSON,Context::get_entries_json), and the token estimate of all three SDKs counts it asJSON.stringifydoes in a tool call's arguments.
BF-61300bb
MCP OAuth reads a non-HTTP redirect or server URL as Node's new URL does: dot segments are removed, a…
- Source: PiG's own defect
- Symptom: MCP OAuth reads a non-HTTP redirect or server URL as Node's
new URLdoes: dot segments are removed, a defaultwsorftpport is dropped, and a malformed percent escape such as/%zzis kept. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
61300bb3e8(2026-10-10) - Tests:
internal/codingagent/interactive_native_login_test.go::TestLoginListsARegisteredNativeAPIKeyProviderInNameOrder,internal/codingagent/session_listing_race_test.go::TestSessionListingSortsConcurrentlyWithoutARace,internal/jsstring/locale_compare_test.go::TestLocaleCompareIsSafeForConcurrentUse,internal/jsstring/locale_compare_test.go::TestLocaleCompareOrdersAsNodeDoes,mcp/oauth/parse_url_node_test.go::TestParseURLMatchesNodeNewURL - Release note: MCP OAuth reads a non-HTTP redirect or server URL as Node's
new URLdoes: dot segments are removed, a defaultwsorftpport is dropped, and a malformed percent escape such as/%zzis kept.
BF-61300bb-2
Sorting names with localeCompare (MCP servers, sessions, login providers, codemode namespaces, model lists…
- Source: PiG's own defect
- Symptom: Sorting names with
localeCompare(MCP servers, sessions, login providers, codemode namespaces, model lists, Hugging Face results) no longer shares one collator between goroutines, which raced when sessions were listed concurrently, and uses ICU root collation for every name instead of an ASCII weight table. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),61300bb3e8(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunkand 6 more in docs/bug-ledger.json - Release note: Sorting names with
localeCompare(MCP servers, sessions, login providers, codemode namespaces, model lists, Hugging Face results) no longer shares one collator between goroutines, which raced when sessions were listed concurrently, and uses ICU root collation for every name instead of an ASCII weight table.
BF-6132277
An MCP OAuth authorization response with an iss parameter is now rejected when no authorization server…
- Source: A fix Pi shipped that PiG ported
- Symptom: An MCP OAuth authorization response with an
issparameter is now rejected when no authorization server metadata was discovered, so a malicious MCP server cannot receive a code another authorization server issued (as Pi c5f5b328 does). - Root cause: Port of Pi main (RFC 9207: without metadata there is no issuer to compare with, so a code from another authorization server could reach the token endpoint). OAuthIssuerMismatchError.Expected is now *string (nil = none), as Pi's string | undefined.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
61322774f1(2026-10-10) - Tests:
mcp/oauth/absent_test.go::TestOAuthIssuerMismatchErrorNamesAMissingIssAsNone,mcp/oauth/absent_test.go::TestOAuthIssuerMismatchErrorQuotesIssuersAsJSONStringify,mcp/oauth/behavior_test.go::TestOAuthErrorMessagesAreTheOnesErrorsTSBuilds,mcp/oauth/error_constructors_upstream_test.go::TestOAuthErrorConstructorsMatchErrorsTs,mcp/oauth/errors_class_test.go::TestOAuthErrorClassesCarryTheirNameAndMessage - Release note: An MCP OAuth authorization response with an
issparameter is now rejected when no authorization server metadata was discovered, so a malicious MCP server cannot receive a code another authorization server issued (as Pi c5f5b328 does).
BF-61fc01d
list_mcp_resources, list_mcp_resource_templates and read_mcp_resource return every member a server sent…
- Source: PiG's own defect
- Symptom: Members PiG did not know were dropped and the known ones were reordered.
- Root cause: Before, members PiG did not know were dropped and the known ones were reordered.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
44e0ab8129(2026-10-05),49cb28c369(2026-10-05),61fc01d975(2026-10-05) - Tests:
coding/mcpext/connection_test.go::TestMCPConnectionsAppendsTheLastUTF16UnitsOfAFailedStdioServersStderr,coding/mcpext/extension_test.go::TestAgentSessionMCPResourceToolsKeepTheMembersAndOrderTheServerSent,coding/mcpext/extension_test.go::TestAgentSessionMCPResourceToolsTrimServerArgumentsLikeJavaScript,coding/mcpext/extension_test.go::TestAgentSessionMCPResourceToolsWriteTheServersEntriesAsJSONStringifyDoes,coding/mcpext/fixtures_test.go::TestMain,coding/mcpext/jstrim_internal_test.go::TestParseStatesTreatsJavaScriptWhitespaceAsEmpty,coding/mcpext/jstrim_internal_test.go::TestResponseFromRedirectURLTrimsLikeJavaScript,coding/mcpext/jstrim_internal_test.go::TestWaitForAuthorizationResponseCancelsOnAJavaScriptBlankAnswerand 5 more in docs/bug-ledger.json - Release note:
list_mcp_resources,list_mcp_resource_templatesandread_mcp_resourcereturn every member a server sent for a resource, template or content entry, in Pi's order and in the formJSON.stringifywrites: integer-like keys first, numbers such as2.0as2, and<,&and>unescaped. Before, members PiG did not know were dropped and the known ones were reordered.
BF-61fc01d-2
MCP tool descriptions, resource MIME types, a pasted sign-in redirect URL and the resource tools' server…
- Source: PiG's own defect
- Symptom: MCP tool descriptions, resource MIME types, a pasted sign-in redirect URL and the resource tools'
serveranduriarguments are trimmed as JavaScript trims them: a byte order mark (U+FEFF) is removed and U+0085 is kept. - Root cause: resources.ts jsonResult (packages/coding-agent/src/extensions/mcp/resources.ts:182-187) returns JSON.stringify(payload) as text and the payload object as structured content, and read (resources.ts:325-333) returns the contents as JSON.parse read them. The listed entries carried the server's raw member bytes, so PiG wrote integer-like keys in sent order, kept
2.0,1e2,\u00e9and escaped<&>as \u003c\u0026\u003e. jsonResult and the read's structured content now pass through jsonstringify.Canonicalize. - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
471c46e063(2026-10-05),49cb28c369(2026-10-05),61fc01d975(2026-10-05) - Tests:
coding/mcpext/extension_test.go::TestAgentSessionMCPHidesToolsInTheOrderTheServerListedThem,coding/mcpext/extension_test.go::TestAgentSessionMCPResourceToolsKeepTheMembersAndOrderTheServerSent,coding/mcpext/extension_test.go::TestAgentSessionMCPResourceToolsTrimServerArgumentsLikeJavaScript,coding/mcpext/extension_test.go::TestAgentSessionMCPResourceToolsWriteTheServersEntriesAsJSONStringifyDoes,coding/mcpext/jstrim_internal_test.go::TestParseStatesTreatsJavaScriptWhitespaceAsEmpty,coding/mcpext/jstrim_internal_test.go::TestResponseFromRedirectURLTrimsLikeJavaScript,coding/mcpext/jstrim_internal_test.go::TestWaitForAuthorizationResponseCancelsOnAJavaScriptBlankAnswer,coding/mcpext/log_rotation_test.go::TestMcpServerLogComparesTheTrackedAndTheCurrentSizeStrictlyWithTheLimitand 4 more in docs/bug-ledger.json - Release note: MCP tool descriptions, resource MIME types, a pasted sign-in redirect URL and the resource tools'
serveranduriarguments are trimmed as JavaScript trims them: a byte order mark (U+FEFF) is removed and U+0085 is kept.
BF-6235532
Filterable selector lists (theme, images, tree-filter-like pickers) check up/down before confirm/cancel like…
- Source: PiG's own defect
- Symptom: Filterable selector lists (theme, images, tree-filter-like pickers) check up/down before confirm/cancel like Pi's SelectList when a key is bound to several of them.
- Root cause: The assignment is removed. SelectSubmenuComponent now has OnSelectionChange (set by newSelectSubmenuStep, its production caller) fired after up/down. Frames compare as text (Pi bold/fg closers vs Pig SGR 0 differ, open renderer question). Overlapping rebinds (a key shared by cancel and up) are not probed: FilterableList checks cancel before up unlike Pi's SelectList; follow-up.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d1f258f335(2026-10-06),082355f166(2026-10-07),0ae3fcf17c(2026-10-07),4058f34548(2026-10-07),623553262c(2026-10-07),a7be906e71(2026-10-07),c8e3d86378(2026-10-07),ccaade27af(2026-10-07),f2a2b3409b(2026-10-07) - Tests:
internal/codingagent/extension_dialog_remapping_test.go::TestExtensionDialogRemappingThroughOwnerLoop,internal/codingagent/remote_overlay_production_review_test.go::TestRemoteOverlayProductionRejectsCachedTerminalWidth,internal/codingagent/session_selector_oracle_test.go::TestSessionSelectorInputMatchesPi,internal/codingagent/settings_submenu_oracle_test.go::TestSettingsSubmenusMatchPi,internal/codingagent/thinking_selector_oracle_test.go::TestThinkingSelectorInputMatchesPi,internal/codingagent/tree_row_format_test.go::TestFormatTreeRow_AllEntryTypes,internal/codingagent/tree_selector_oracle_test.go::TestTreeSelectorInputMatchesPi,internal/codingagent/trust_selector_oracle_test.go::TestTrustSelectorInputMatchesPiand 16 more in docs/bug-ledger.json - Release note: Filterable selector lists (theme, images, tree-filter-like pickers) check up/down before confirm/cancel like Pi's SelectList when a key is bound to several of them.
BF-6258d27
fix(rpc): a replacement aborts the compactions of its batch at its teardown only
- Source: PiG's own defect
- Symptom: fix(rpc): a replacement aborts the compactions of its batch at its teardown only
- Root cause: A compact yields after compaction_start until every replacement requested by the end of its batch tore down (which cancels it) or gave up, as in Pi where the teardown reaches dispose before any compaction I/O. A refused or failed replacement (fork of an unknown entry, switch_session to a directory) leaves the compaction alone and Pi writes the entry. The compaction-cancel registry was keyed by new(struct{}), which every compact shared, so only the last compact of a batch could be cancelled. An abort before the summary request is Pi's getAuth rejection with the signal's reason (prepareSummarizationRequest), not a summarizer error.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
6258d2784c(2026-10-10) - Tests:
coding/cli/rpc_replacement_order_test.go::TestRPCRefusedReplacementLeavesTheCompactionOfItsBatchAlone,coding/cli/rpc_replacement_order_test.go::TestRPCReplacementAbortsTheCompactionOfItsBatch,coding/session_test.go::TestCompactCancelsSynchronouslyFromStartEvent
BF-625a32e
Display text that PiG cuts at a length (the /tree listing, the Piglet system-prompt preview, an extension…
- Source: PiG's own defect
- Symptom: Display text that PiG cuts at a length (the
/treelisting, the Piglet system-prompt preview, an extension load error cause) is cut at characters, not bytes, so a multi-byte character at the cut no longer becomes invalid UTF-8. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
625a32e078(2026-10-10) - Tests:
coding/extension/host/subprocess/load_error_cause_test.go::TestStderrCauseCutsAtCharactersNotBytes,internal/codingagent/tree_ascii_truncation_test.go::TestRenderTreeASCIICutsMessagesAtCharactersNotBytes - Release note: Display text that PiG cuts at a length (the
/treelisting, the Piglet system-prompt preview, an extension load error cause) is cut at characters, not bytes, so a multi-byte character at the cut no longer becomes invalid UTF-8.
BF-626dccc
OpenAI-compatible Chat Completions requests resolve chat_template_args (the baseten thinking format) and…
- Source: PiG's own defect
- Symptom: OpenAI-compatible Chat Completions requests resolve
chat_template_args(thebasetenthinking format) andchat_template_kwargs(thechat-templateformat) as Pi does. - Root cause: openai-completions.ts:1034-1075 resolves compat.chatTemplateArgs (baseten, line 916) and compat.chatTemplateKwargs (chat-template, line 906) with one function: {$var:thinking.budget} is the clamped thinking budget, and {$var:thinking.effort} is thinkingLevelMap[level] (the level when absent, nothing when null) or thinkingLevelMap.off when reasoning is off, nothing unless it is a string. PiG's baseten branch re-implemented the loop without thinking.budget, so a budget variable was dropped there, and both formats sent "none" for an effort variable when reasoning was off with no off entry, and sent the raw level for a null entry. Baseten now calls resolveChatTemplateValues like chat-template…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
626dccc66c(2026-10-10) - Tests:
ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPi,ai/openai_completions_chat_template_values_test.go::TestOpenAICompletionsChatTemplateEffortFollowsTheThinkingLevelMap,ai/openai_completions_chat_template_values_test.go::TestOpenAICompletionsChatTemplateValuesResolveTheSameForKwargsAndArgs,ai/thinking_level_map_clamp_test.go::TestThinkingLevelMapDecidesSupportedAndClampedLevels - Release note: OpenAI-compatible Chat Completions requests resolve
chat_template_args(thebasetenthinking format) andchat_template_kwargs(thechat-templateformat) as Pi does. Athinking.budgetvariable inchat_template_argsnow sends the clamped thinking budget instead of being dropped. Athinking.effortvariable sends the model'sthinkingLevelMap.offvalue when reasoning is off and is left out when that entry is absent or not a string, instead of sending"none". Anullentry for the requested level also leaves it out, instead of sending the level name.
BF-62a1c87
The ExtensionSelector and ExtensionInput oracles also run with the Kitty keyboard protocol active (CSI-u…
- Source: PiG's own defect
- Symptom: The
ExtensionSelectorandExtensionInputoracles also run with the Kitty keyboard protocol active (CSI-u enter, escape, ctrl+j, ctrl+c, shift+enter, letters, modified arrows) in Pi and Pig; no difference. - Root cause: No difference.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
62a1c87b2a(2026-10-11) - Tests:
internal/codingagent/settings_selector_oracle_test.go::TestSettingsSelectorMatchesPi,tui/extension_input_oracle_test.go::TestExtensionInputMatchesPi,tui/extension_selector_oracle_test.go::TestExtensionSelectorMatchesPi - Release note: The
ExtensionSelectorandExtensionInputoracles also run with the Kitty keyboard protocol active (CSI-u enter, escape, ctrl+j, ctrl+c, shift+enter, letters, modified arrows) in Pi and Pig; no difference.
BF-62ae30d
Durable: reading the history of a conversation whose parent conversation is not stored now fails with Pi's…
- Source: PiG's own defect
- Symptom: Durable: reading the history of a conversation whose parent conversation is not stored now fails with Pi's error ("Cannot read properties of undefined (reading 'parent')") at the point where Pi's lineage walk reaches the missing record, instead of returning a truncated history.
- Root cause: Differences found: every message used Go's %q where Pi interpolates the name between plain double quotes (constrained-sampling.ts:136-150,188,226,259,267), so a tool named 'we"ird' got backslashes; isStructuredSchema tested properties/items with != nil although JS tests !== undefined (an explicit null counts) and searched a type array through toStringSlice, which gave up when any element was not a string where Array.includes does not. Mutants killed: each of those, the delta no-op and closed-buffer rules, the JS trim of grammar variants, the nullable-wrap and required checks. Go cannot hold a non-object tool.parameters, so boolean/array/string roots are not probed (type-level difference).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),97e178f7f6(2026-10-09),1dcaef738b(2026-10-10),62ae30d413(2026-10-10),749840b14d(2026-10-10),752c93fb19(2026-10-10),f71185dc7a(2026-10-10) - Tests:
ai/constrained_sampling_oracle_test.go::TestConstrainedSamplingMatchesPi,ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,durable/storage/jsonl/recovery_rewrite_test.go::TestJsonlRecoveryIgnoresALeadingByteOrderMarkOnEachLine,durable/storage/jsonl/recovery_rewrite_test.go::TestJsonlRecoveryRewritesReclaimedSidecarRecordsCanonically,durable/storage/jsonl_pi_oracle_test.go::TestJsonlStorageMatchesPiOnSeededPrograms,durable/storage/jsonl_recovery_pi_oracle_test.go::TestJsonlRecoveryMatchesPiOnDamagedDirectories,durable/storage/memory_dangling_parent_test.go::TestMemoryStorageLineageThroughAMissingParentConversationFails,durable/storage/memory_pi_oracle_test.go::TestMemoryStorageMatchesPiOnSeededProgramsand 2 more in docs/bug-ledger.json - Release note: Durable: reading the history of a conversation whose parent conversation is not stored now fails with Pi's error ("Cannot read properties of undefined (reading 'parent')") at the point where Pi's lineage walk reaches the missing record, instead of returning a truncated history. The SQLite storage reports the same failure with Pi's SQLite error ("Cannot read properties of undefined (reading 'id')") instead of "Fork parent conversation is missing".
BF-62c3691
grep with a large context no longer holds every context line in memory: its memory is bounded by the 50KB…
- Source: PiG's own defect
- Symptom:
grepwith a largecontextno longer holds every context line in memory: its memory is bounded by the 50KB output it returns, with the same text and truncation details (#9276). - Root cause: grep formats limit x (2*context+1) lines, joins them and keeps the first 50KB (Pi grep.ts:176-282). A large context therefore held memory proportional to limit x file for a fixed-size result: 65 MB at a 2,000-line file, out of memory on large files.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
10aebd5c05(2026-10-09),62c3691863(2026-10-10, #9276),82d1808c64(2026-10-10) - Tests:
ai/complexity_scaling_norace_test.go::TestScalingProviderRequestPerTurn,ai/complexity_scaling_norace_test.go::TestScalingProviderStreamPerDelta,coding/cli/complexity_scaling_norace_test.go::TestScalingExtensionEventStatePush,coding/complexity_scaling_norace_test.go::TestScalingSessionTurn,coding/extension/host/subprocess/complexity_scaling_norace_test.go::TestScalingMessageUpdatePayload,coding/session_context_usage_reuse_test.go::TestContextUsageReusesTheEstimateUntilTheLogOrWindowChanges,internal/codingagent/compaction/complexity_scaling_norace_test.go::TestScalingCompactionAndContextEstimate,internal/codingagent/complexity_scaling_norace_test.go::TestScalingSessionLogOperationsand 10 more in docs/bug-ledger.json - Release note:
grepwith a largecontextno longer holds every context line in memory: its memory is bounded by the 50KB output it returns, with the same text and truncation details (#9276).
BF-6323c8e
fix(ai): a Bedrock model outside the catalog is a bedrock-converse-stream model of amazon-bedrock, so its own…
- Source: PiG's own defect
- Symptom: fix(ai): a Bedrock model outside the catalog is a bedrock-converse-stream model of amazon-bedrock, so its own replay keeps tool-call ids (the fallback Model had no provider or api, so isSameModel was false and bedrock-converse-stream.ts' normalizeToolCallId rewrote them).
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
6323c8eb2f(2026-10-10) - Tests:
ai/bedrock_uncataloged_model_test.go::TestBedrockUncatalogedModelIsTheSameModelAsItsOwnReplay
BF-63b4fc8
NodeExecutionEnv.Watch gives a directory replaced at the same path a new watcher, reports changes to the…
- Source: A fix Pi shipped that PiG ported
- Symptom:
NodeExecutionEnv.Watchgives a directory replaced at the same path a new watcher, reports changes to the file a symbolic-link target points to, no longer lets a non-recursive target stop an overlapping recursive target from covering subdirectories, and installs no watcher once closed. - Root cause: createPowerShellTool over argv exec; read keeps a log that grows while read; OutputBuffer drops a byte-order mark only at the start of the output; NodeExecutionEnv.watch fails with permission_denied, follows a symbolic link to a file, keeps recursive coverage under an overlapping non-recursive target, re-installs a watcher on a replaced directory and installs none after Close; the env conformance suite gains the three watch checks (packages/durable 1.0.4).
- User impact: Behavior differed from Pi
- Platform: macOS
- Fixed in: 0.5.0
- Fix commits:
63b4fc80e7(2026-10-05) - Tests:
chord/output/output_test.go::TestOutputBuffer,chord/output/output_test.go::TestToolOutputBounds,durable/harness/harness_registry_test.go::TestSettingsKeepTheDefaultOfAProgressIntervalGivenAsUndefined,durable/tools/powershell_upstream_test.go::TestPowerShellFallsBackToWindowsPowerShellAndReportsTheLastStartFailure,durable/tools/powershell_upstream_test.go::TestPowerShellRunsRealPowerShellWithUTF8Output,durable/tools/powershell_upstream_test.go::TestPowerShellRunsTheCommandWithPwshAsOneArgumentForcingUTF8Output,durable/tools/powershell_upstream_test.go::TestReadReadsALogThatGrowsWhileItIsRead - Release note:
NodeExecutionEnv.Watchgives a directory replaced at the same path a new watcher, reports changes to the file a symbolic-link target points to, no longer lets a non-recursive target stop an overlapping recursive target from covering subdirectories, and installs no watcher once closed. On macOS it reports changes to the file that a watched symbolic link points to.
BF-63bb0b7
pig extension upgrade now rewrites a Context getter call that is the only argument of a variadic call…
- Source: PiG's own defect
- Symptom:
pig extension upgradenow rewrites aContextgetter call that is the only argument of a variadic call, such asfmt.Println(ctx.ConfigHome()). - Root cause: That call still compiles against the current SDK because the value and the error both become arguments, so the error was printed or passed on instead of handled and the upgrade reported nothing.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
63bb0b7eac(2026-10-12),974050757c(2026-10-12),d305b76bda(2026-10-12) - Tests:
coding/cli/extension_upgrade_test.go::TestUpgraderRestoresEveryFileWhenTheRebuildFails,coding/cli/extension_upgrade_test.go::TestUpgraderRestoresWrittenFilesWhenAWriteFails,coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideKeepsCurrentStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestRustSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/subprocess/builder_test.go::TestGoNeedsStagedSDK,coding/extension/host/subprocess/builder_test.go::TestRustNeedsStagedSDK,extensions/sdk/upgrade/fixtures_test.go::TestFixturesUpgradeAndBuildand 3 more in docs/bug-ledger.json - Release note:
pig extension upgradenow rewrites aContextgetter call that is the only argument of a variadic call, such asfmt.Println(ctx.ConfigHome()). That call still compiles against the current SDK because the value and the error both become arguments, so the error was printed or passed on instead of handled and the upgrade reported nothing. It is now rewritten like the other uses: the function returns the error, or, forConfigHomein a function that does not return an error, the line is listed for you to change.
BF-644a9f2
Changelog link rewriting and git source parsing match a URL scheme case-insensitively as Pi's regex with…
- Source: PiG's own defect
- Symptom: Changelog link rewriting and
gitsource parsing match a URL scheme case-insensitively as Pi's regex with theiflag does:ſ(U+017F) andK(U+212A) no longer count assandk. - Root cause: Regenerated with make bug-ledger.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
644a9f2fde(2026-10-13) - Tests:
test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Changelog link rewriting and
gitsource parsing match a URL scheme case-insensitively as Pi's regex with theiflag does:ſ(U+017F) andK(U+212A) no longer count assandk.
BF-644a9f2-2
A Node extension's ctx.ui.custom factory can call tui.showOverlay, tui.hideOverlay and tui.setFocus…
- Source: PiG's own defect
- Symptom: A Node extension's
ctx.ui.customfactory can calltui.showOverlay,tui.hideOverlayandtui.setFocus, as in Pi. - Root cause: Regenerated with make bug-ledger.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
644a9f2fde(2026-10-13) - Tests:
test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: A Node extension's
ctx.ui.customfactory can calltui.showOverlay,tui.hideOverlayandtui.setFocus, as in Pi.showOverlayshows the component as a raw overlay at once and returns a synchronous handle (hide,setHidden,isHidden,focus,unfocus,isFocused,getBounds), so Pi's overlay-focus, overlay-passive and overlay-streaming example extensions work.tui.requestRender()redraws every component the extension shows. A raw overlay ends with its handle'shide(), or when the extension's process exits or reloads.
BF-644b3e0
A codemode script that calls text() tens of thousands of times no longer blocks the process: adjacent text…
- Source: PiG's own defect
- Symptom: A codemode script that calls
text()tens of thousands of times no longer blocks the process: adjacent text items are joined in linear time (Pi #10678). - Root cause: execute.ts joinAdjacentText rebuilds the joined string per item; the port did too. 40k items allocated 28 GB (11 s); now linear. Output identical (differential test vs the per-item rebuild).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
644b3e0a2a(2026-10-09) - Tests:
coding/extension/builtin/codemode/join_adjacent_text_test.go::TestJoinAdjacentTextIsLinearInTheNumberOfItems,coding/extension/builtin/codemode/join_adjacent_text_test.go::TestJoinAdjacentTextMatchesUpstreamOnMixedItems - Release note: A codemode script that calls
text()tens of thousands of times no longer blocks the process: adjacent text items are joined in linear time (Pi #10678).
BF-648ff3e
A child session's message_update event carries the message so far and the assistant event's partial, as…
- Source: PiG's own defect
- Symptom: A child session's
message_updateevent carries the message so far and the assistant event'spartial, as Pi'sAgentSession.subscribehands them to a listener, where RPC mode's trimmed shape left both out. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A child session's
message_updateevent carries the message so far and the assistant event'spartial, as Pi'sAgentSession.subscribehands them to a listener, where RPC mode's trimmed shape left both out.
BF-648ff3e-10
pig --list-models now prints only the models the model runtime reports as available, as Pi does
- Source: PiG's own defect
- Symptom: It used to append every bundled model of each provider that had a login or API key, so after
pig update --modelsit still listed OpenRouter models that the saved personalized catalog had dropped, such asopenrouter/autoand the:batchvariants. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
pig --list-modelsnow prints only the models the model runtime reports as available, as Pi does. It used to append every bundled model of each provider that had a login or API key, so afterpig update --modelsit still listed OpenRouter models that the saved personalized catalog had dropped, such asopenrouter/autoand the:batchvariants.
BF-648ff3e-100
An SDK's ctx.hasPendingMessages() in interactive mode counts queued user messages only, as Pi does, and no…
- Source: PiG's own defect
- Symptom: An SDK's
ctx.hasPendingMessages()in interactive mode counts queued user messages only, as Pi does, and no longer counts the custom messages an extension queued withsendMessage. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: An SDK's
ctx.hasPendingMessages()in interactive mode counts queued user messages only, as Pi does, and no longer counts the custom messages an extension queued withsendMessage.
BF-648ff3e-101
A Node extension that changes the active tools or registers a tool now reads the system prompt back as Pi does
- Source: PiG's own defect
- Symptom: A Node extension that changes the active tools or registers a tool now reads the system prompt back as Pi does.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A Node extension that changes the active tools or registers a tool now reads the system prompt back as Pi does.
ctx.getSystemPrompt()andctx.getSystemPromptOptions()answered from the state the host pushes with the next event, so a read afterpi.setActiveToolsorpi.registerToolin the same handler returned the prompt of before the write, while the Go, Python and Rust SDKs read the live value. From the first such write on, the reads ask the host.
BF-648ff3e-102
ctx.isIdle() after pi.sendMessage(..., { triggerTurn: true }) in an agent_settled handler of a Node…
- Source: PiG's own defect
- Symptom:
ctx.isIdle()afterpi.sendMessage(..., { triggerTurn: true })in anagent_settledhandler of a Node extension now reportstrue, as the Go, Python and Rust SDKs do. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
ctx.isIdle()afterpi.sendMessage(..., { triggerTurn: true })in anagent_settledhandler of a Node extension now reportstrue, as the Go, Python and Rust SDKs do. The session holds such a message until the handlers return, as in Pi, and Node had assumed the run started.
BF-648ff3e-103
A Node extension's ctx.ui.select, confirm and input honor options.signal, as Pi's RPC and interactive…
- Source: PiG's own defect
- Symptom: The signal was ignored: the dialog stayed open until the user or its timeout answered, and the extension waited for it.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A Node extension's
ctx.ui.select,confirmandinputhonoroptions.signal, as Pi's RPC and interactive modes do. A signal that is already aborted resolves the default (undefined, orfalseforconfirm) without showing the dialog or writing an RPC request, and a signal that aborts while the dialog is open resolves the default at once and dismisses the dialog (RPC: the pending request is dropped). Before, the signal was ignored: the dialog stayed open until the user or its timeout answered, and the extension waited for it.
BF-648ff3e-104
A start could fail with a bare missing-file error when another pig's cache collection removed the cached…
- Source: PiG's own defect
- Symptom: A start could fail with a bare missing-file error when another
pig's cache collection removed the cached extension build it had just reused. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A start could fail with a bare missing-file error when another
pig's cache collection removed the cached extension build it had just reused. The collection (the daily automatic one, orpig extensions cache prune) decides which entries are expired and removes them a moment later. A start that reuses an old entry in between refreshed its use time but holds no lease until its extension process spawns, so the entry was removed under it, the lease then came back empty instead of failing, and the spawn reported a missing file. The collection now reads each entry again under its exclusive lock and skips one that anotherpigused since, counting it as in use. A reuse takes the entry's lock while it checks and records the entry, so it never reuses an entry that is being removed and builds it again instead. A lease for an entry that is gone fails withcache_lease_failedand says the entry was removed. The SDK fingerprint file of a published entry is replaced through a rename and written only when it changes, so a collection can no longer read it half written and expire a build that a start is using.
BF-648ff3e-105
A tool's prepareLoadout hook now runs again before every later turn of a run, as in Pi, so the descriptions…
- Source: PiG's own defect
- Symptom: A tool's
prepareLoadouthook now runs again before every later turn of a run, as in Pi, so the descriptions it sets and the declarations it hides follow the state the hook reads. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A tool's
prepareLoadouthook now runs again before every later turn of a run, as in Pi, so the descriptions it sets and the declarations it hides follow the state the hook reads. The hooks ran when a run started and when the loadout changed, so a hook that read settings or extension state kept the first turn's answer for the rest of the run.
BF-648ff3e-106
Fixed the chord host keeping a disposed object reachable until its ready timeout (a minute by default) had…
- Source: PiG's own defect
- Symptom: Fixed the chord host keeping a disposed object reachable until its ready timeout (a minute by default) had passed: a stopped host timer stayed in the runtime's timer heap with its callback, and the callback captured the object.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Fixed the chord host keeping a disposed object reachable until its ready timeout (a minute by default) had passed: a stopped host timer stayed in the runtime's timer heap with its callback, and the callback captured the object. A stopped timer now releases its callback at once.
BF-648ff3e-107
The /tree "Summarize branch?" selector and "Custom summarization instructions" editor, the /import…
- Source: PiG's own defect
- Symptom: The /tree "Summarize branch?" selector and "Custom summarization instructions" editor, the /import confirmation and the missing-session-cwd confirmation of /resume and /import are dialogs on the same stack as an extension's, as in Pi.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The /tree "Summarize branch?" selector and "Custom summarization instructions" editor, the /import confirmation and the missing-session-cwd confirmation of /resume and /import are dialogs on the same stack as an extension's, as in Pi. A dialog an extension opens while one of them is shown takes the keys, closing the prompt shows the earlier dialog again instead of hiding it, each reports its blocked status to the terminal, and a session replacement or
/reloadremoves the prompt with the other dialogs./bugkeeps its own prompts outside the stack and no longer reports a blocked status for its description editor, as in Pi (Pi 38847563, bug-report.ts).
BF-648ff3e-108
OpenAI Responses requests keep the prompt cache across tool changes (Pi #11142)
- Source: PiG's own defect
- Symptom: OpenAI Responses requests keep the prompt cache across tool changes (Pi #11142).
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: OpenAI Responses requests keep the prompt cache across tool changes (Pi #11142). Every tool is declared in
additional_toolsitems from the head of the input, a removal is announced as text with the still-unavailable tools restated, and synthetic tool search and Kimi tool messages move the top-level tool list only at the last removal or redefinition, so a later tool load no longer rewrites the cached prefix. Applies toopenai-responses,azure-openai-responses,openai-codex-responsesand Kimi onopenai-completions.
BF-648ff3e-109
On a file system that refuses file locks (an NFS mount without a lock manager, a FUSE or SMB share), starting…
- Source: PiG's own defect
- Symptom: On a file system that refuses file locks (an NFS mount without a lock manager, a FUSE or SMB share), starting an extension failed with
wait for cell build lock: flock: no locks available, or with a bare lock error from the lease of a cached build, the cache collector or the SDK staging, and nothing named the cause. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: On a file system that refuses file locks (an NFS mount without a lock manager, a FUSE or SMB share), starting an extension failed with
wait for cell build lock: flock: no locks available, or with a bare lock error from the lease of a cached build, the cache collector or the SDK staging, and nothing named the cause. The error now names the directory, says the file system does not support file locks, and names the remedy: pointPIG_HOMEat a directory on a file system that does. The cache still needs its locks, so a file system without them cannot hold it.
BF-648ff3e-11
/reload no longer reports `Extension "builtin:codemode" error: This extension ctx is stale after session…
- Source: PiG's own defect
- Symptom:
/reloadno longer reportsExtension "builtin:codemode" error: This extension ctx is stale after session replacement or reload.A tool registered while an in-process extension factory runs refreshed the live Session, which ran theprepareLoadouthook of the previous load's tool through the API the reload had just retired. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
/reloadno longer reportsExtension "builtin:codemode" error: This extension ctx is stale after session replacement or reload.A tool registered while an in-process extension factory runs refreshed the live Session, which ran theprepareLoadouthook of the previous load's tool through the API the reload had just retired. Like Pi's loading runtime, whoserefreshToolsis a no-op until the runner binds, a registration during factory load now leaves the Session alone and the reload's own rebuild declares the tools once.
BF-648ff3e-110
In RPC mode, a toast an extension deferred with setTimeout(0) from a model_select or session_start…
- Source: PiG's own defect
- Symptom: In RPC mode, a toast an extension deferred with
setTimeout(0)from amodel_selectorsession_startlistener reached the output before the response of thenew_session,fork,clone,switch_session,set_modelorcycle_modelcommand that ran the listener, or of the prompt whose extension command started the new Session. - Root cause: Pi writes the response first, because its response follows the listeners in microtasks and the timer needs a later turn of the event loop.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: In RPC mode, a toast an extension deferred with
setTimeout(0)from amodel_selectorsession_startlistener reached the output before the response of thenew_session,fork,clone,switch_session,set_modelorcycle_modelcommand that ran the listener, or of the prompt whose extension command started the new Session. Pi writes the response first, because its response follows the listeners in microtasks and the timer needs a later turn of the event loop. The host now holds such UI calls (ui.notify,ui.setStatus,ui.setWidget,ui.setTitle,ui.setEditorText) until the command's response is written. Seen with@liyu1981/pi-tweaks' remember-model extension.
BF-648ff3e-111
In RPC mode, a message an extension's session_start listener sent while a command replaced the Session was…
- Source: PiG's own defect
- Symptom: In RPC mode, a message an extension's
session_startlistener sent while a command replaced the Session was written to the output. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: In RPC mode, a message an extension's
session_startlistener sent while a command replaced the Session was written to the output. Pi subscribes its output writer aftersession_start, so it writes nothing for it; PiG now publishes the new Session's events aftersession_starttoo.
BF-648ff3e-112
In RPC mode, an extension command that sends a user message and then awaits ctx.waitForIdle() (the init…
- Source: PiG's own defect
- Symptom: In RPC mode, an extension command that sends a user message and then awaits
ctx.waitForIdle()(the init command of a Pigpen warden) had the run'sagent_startwritten before the prompt's response, for the Go, Python, Rust and Node SDKs alike. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: In RPC mode, an extension command that sends a user message and then awaits
ctx.waitForIdle()(the init command of a Pigpen warden) had the run'sagent_startwritten before the prompt's response, for the Go, Python, Rust and Node SDKs alike. Pi'swaitForIdlereturns at once while the run the command started has not begun, so the command returns, the response is written, andagent_startfollows. The host now settles such a wait at once and no longer counts the handler as suspended.
BF-648ff3e-113
Two Bun runtime installs, or an install and a status check, in different processes no longer leave a moment…
- Source: PiG's own defect
- Symptom: Two Bun runtime installs, or an install and a status check, in different processes no longer leave a moment where
<agent dir>/runtimes/typescript/bundoes not exist:/bun-runtimeinstall, remove, verify and status take a lock on the slot, and an install removes thebun.old-*directory that a killed install left behind. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Two Bun runtime installs, or an install and a status check, in different processes no longer leave a moment where
<agent dir>/runtimes/typescript/bundoes not exist:/bun-runtimeinstall, remove, verify and status take a lock on the slot, and an install removes thebun.old-*directory that a killed install left behind.
BF-648ff3e-114
A slow toolchain version check no longer blocks other starts without limit
- Source: PiG's own defect
- Symptom: A slow toolchain version check no longer blocks other starts without limit.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A slow toolchain version check no longer blocks other starts without limit. Go, Rust and Python extension builds probe
go version,rustc --versionand the like; the probe is now ended after two minutes, a start that finds another start probing the same tool waits at most five minutes before probing for itself, and two projects with differentgo.modorrust-toolchain.tomlselectors each keep their own cached version instead of probing again on every alternate start.
BF-648ff3e-115
Fixed the chord host manager letting other code read it as stopped, or start the child again, before the…
- Source: PiG's own defect
- Symptom: Fixed the chord host manager letting other code read it as stopped, or start the child again, before the
onExitcallback for the child that just exited had run: upstream sets the status and callsonExitin one step, so the manager now keeps other goroutines waiting untilonExitreturns, whileonExititself sees it stopped and may start the child again. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Fixed the chord host manager letting other code read it as stopped, or start the child again, before the
onExitcallback for the child that just exited had run: upstream sets the status and callsonExitin one step, so the manager now keeps other goroutines waiting untilonExitreturns, whileonExititself sees it stopped and may start the child again.
BF-648ff3e-116
Fixed a chord link closed with a reason reporting "peer closed" to its close handlers instead when the peer…
- Source: PiG's own defect
- Symptom: Fixed a chord link closed with a reason reporting "peer closed" to its close handlers instead when the peer answered the goodbye before the closing side had marked the link closed; the link now ends first, with the reason it was closed with, as upstream's does.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Fixed a chord link closed with a reason reporting "peer closed" to its close handlers instead when the peer answered the goodbye before the closing side had marked the link closed; the link now ends first, with the reason it was closed with, as upstream's does.
BF-648ff3e-119
Concurrent extension select, confirm, input, editor dialogs and inline custom() components in…
- Source: PiG's own defect
- Symptom: Concurrent extension
select,confirm,input,editordialogs and inlinecustom()components in interactive mode no longer replace each other: a second dialog used to take the editor slot and leave the first one's promise pending. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Concurrent extension
select,confirm,input,editordialogs and inlinecustom()components in interactive mode no longer replace each other: a second dialog used to take the editor slot and leave the first one's promise pending. They stack, the last one is visible, closing it shows the previous one again, and a hidden dialog's abort or timeout removes only itself./reloadand session replacement dispose every stacked dialog, and an editor-slot custom component restores the editor text it found (Pi 38847563, #11166, #9909).
BF-648ff3e-12
Prompts submitted after /scoped-models refreshed the model catalogs and Ctrl+P switched the model no longer…
- Source: PiG's own defect
- Symptom: Prompts submitted after
/scoped-modelsrefreshed the model catalogs and Ctrl+P switched the model no longer fail with "No model selected". - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Prompts submitted after
/scoped-modelsrefreshed the model catalogs and Ctrl+P switched the model no longer fail with "No model selected". The scope update installed catalog descriptors that carry no backend; it now installs the Session's runtime-bound models, as Pi does.
BF-648ff3e-120
Pasting in the shell no longer hangs after copying text in pig and suspending it with Ctrl+Z on Linux: the…
- Source: PiG's own defect
- Symptom: Pasting in the shell no longer hangs after copying text in pig and suspending it with Ctrl+Z on Linux: the
wl-copy,xcliporxselprocess that keeps serving the clipboard now runs in its own session and is not stopped with pig (Pi f8ab362c, #11179). - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: Linux
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Pasting in the shell no longer hangs after copying text in pig and suspending it with Ctrl+Z on Linux: the
wl-copy,xcliporxselprocess that keeps serving the clipboard now runs in its own session and is not stopped with pig (Pi f8ab362c, #11179).
BF-648ff3e-123
mcp.json files that start with a UTF-8 BOM, which Windows PowerShell 5.1 adds when it rewrites a file, load…
- Source: PiG's own defect
- Symptom:
mcp.jsonfiles that start with a UTF-8 BOM, which Windows PowerShell 5.1 adds when it rewrites a file, load their MCP servers again, andpig mcp addandpig mcp removework on them (Pi bdd9f039, #11191). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
mcp.jsonfiles that start with a UTF-8 BOM, which Windows PowerShell 5.1 adds when it rewrites a file, load their MCP servers again, andpig mcp addandpig mcp removework on them (Pi bdd9f039, #11191).
BF-648ff3e-124
A Go or Rust extension edited while it was being built could be cached under the hash of the source from…
- Source: PiG's own defect
- Symptom: A Go or Rust extension edited while it was being built could be cached under the hash of the source from before the edit.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A Go or Rust extension edited while it was being built could be cached under the hash of the source from before the edit. The compiler reads the source after the build hashed it, past the wait for another
pig's build of the same extension and through its own compile, and nothing read it again, so an edit saved in that time was compiled into a build stored under the original source's hash. Putting the original source back then ran the build of the edit. A build now reads its inputs again when the compiler returns (the extension source, the SDK, workspace modules and toolchain) and caches nothing when they changed: it fails withchanged while it was being built, and the next start builds the current source. A Rust crate'sCargo.lock, whichcargowrites during the build, does not count as an edit.
BF-648ff3e-125
A Node extension that registers or unregisters a provider through ctx.modelRegistry (or…
- Source: PiG's own defect
- Symptom: A Node extension that registers or unregisters a provider through
ctx.modelRegistry(orpi.registerProvider/unregisterProvider) sees the write on the next line, as Pi does:find,getAll,getAvailable,hasConfiguredAuth,getProviderAuthStatusandisUsingOAuthread the registry the registration left, and an unregistration removes the provider's models (a built-in provider keeps its own). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A Node extension that registers or unregisters a provider through
ctx.modelRegistry(orpi.registerProvider/unregisterProvider) sees the write on the next line, as Pi does:find,getAll,getAvailable,hasConfiguredAuth,getProviderAuthStatusandisUsingOAuthread the registry the registration left, and an unregistration removes the provider's models (a built-in provider keeps its own). The runtime keeps its own writes over the host's registry snapshot until the host answers the call, so a publication that predates the write cannot undo it.
BF-648ff3e-126
ctx.abort() aborts the run's ctx.signal, and the signal a tool receives, before it returns in a Node…
- Source: PiG's own defect
- Symptom:
ctx.abort()aborts the run'sctx.signal, and the signal a tool receives, before it returns in a Node extension, as Pi'sagent.abort()does. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
ctx.abort()aborts the run'sctx.signal, and the signal a tool receives, before it returns in a Node extension, as Pi'sagent.abort()does. The host sends the run'srun_signalframe ahead of the reply to theabortcall, so the Go, Python and Rust SDKs read the signal aborted as soon asabort()returns instead of when a watcher's frame happens to win the race.
BF-648ff3e-127
pi.getCommands() in a Node extension lists a command registered after load by the same turn's…
- Source: PiG's own defect
- Symptom:
pi.getCommands()in a Node extension lists a command registered after load by the same turn'spi.registerCommand, as Pi's live command Map does. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
pi.getCommands()in a Node extension lists a command registered after load by the same turn'spi.registerCommand, as Pi's live command Map does.
BF-648ff3e-128
A Node extension tool that awaits real work and then takes the file mutation queue (the upstream subagent…
- Source: PiG's own defect
- Symptom: A Node extension tool that awaits real work and then takes the file mutation queue (the upstream
subagentexample, which writes a prompt file underwithFileMutationQueueafter amkdtemp) no longer waits forever when it runs outside the dispatcher's parallel batch of an agent run, for instance when a tool is called directly or its batch runs sequentially. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A Node extension tool that awaits real work and then takes the file mutation queue (the upstream
subagentexample, which writes a prompt file underwithFileMutationQueueafter amkdtemp) no longer waits forever when it runs outside the dispatcher's parallel batch of an agent run, for instance when a tool is called directly or its batch runs sequentially. The runtime holds a tool's next queue call after it reports that the handler awaits, until the host says the edit and write calls waiting for the tool's start window registered; a tool that holds no start window had nobody waiting, and the host never said so.
BF-648ff3e-129
RPC mode: an extension_ui_response whose value is not a string, whose confirmed is not a boolean, or…
- Source: PiG's own defect
- Symptom: RPC mode: an
extension_ui_responsewhosevalueis not a string, whoseconfirmedis not a boolean, or whosecancelledis not a boolean no longer leaves the dialog pending. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: RPC mode: an
extension_ui_responsewhosevalueis not a string, whoseconfirmedis not a boolean, or whosecancelledis not a boolean no longer leaves the dialog pending. The dialog resolves with the client's JSON as Pi's does: a Node, Go, Python or Rust extension sees a number,null, an object or an array fromselect,inputandeditor, and the client'sconfirmedfromconfirm;cancelledis read by JavaScript truthiness.
BF-648ff3e-13
Two parallel edit calls through two names of one existing file (a hard link, or a case alias on a…
- Source: A Pi defect that PiG fixed first (divergence D190)
- Symptom: Two parallel
editcalls through two names of one existing file (a hard link, or a case alias on a case-insensitive filesystem such as the macOS default) no longer lose one of the edits. - Root cause: both edits of two parallel
editcalls through two names of one file read the same original text and each writes the whole file, so the later write silently drops the earlier edit while both calls report success. - User impact: Lost or altered data
- Platform: macOS
- Fixed in: 0.5.2
- Pi issue: none recorded
- Removed when Pi fixes it: Pi orders hard-link aliases (and, on a case-insensitive filesystem where its
realpathkeeps case, case aliases) by file identity, or an owner decision reverses this one; then the identity check, its marker, the allowance test and this record go. - Fix commits:
2f03eb55db(2026-10-22, #288),648ff3e2b0(2026-10-22, #288) - Tests:
internal/codingagent/tools/mutation_queue_alias_test.go::TestEditPreservesParallelAliasChanges,internal/codingagent/tools/mutation_queue_alias_test.go::TestHardLinkAliasesDifferFromPi,internal/codingagent/tools/mutation_queue_alias_test.go::TestMutationAliasReservationsKeepDispatchOrder,internal/codingagent/tools/mutation_queue_oracle_test.go::TestWithFileMutationQueueMatchesPi - Release note: Two parallel
editcalls through two names of one existing file (a hard link, or a case alias on a case-insensitive filesystem such as the macOS default) no longer lose one of the edits. The built-in file mutation queue made them run at once, so both read the original text and the later write dropped the earlier edit while both calls reported success; it now orders them in call order. Pi does not order hard-link aliases (D190).
BF-648ff3e-130
A YAML date used as a mapping key is named with the time zone names of Node 24.22 and later, the Node Pi runs…
- Source: PiG's own defect
- Symptom: A YAML date used as a mapping key is named with the time zone names of Node 24.22 and later, the Node Pi runs on (tz 2026e).
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A YAML date used as a mapping key is named with the time zone names of Node 24.22 and later, the Node Pi runs on (tz 2026e). Morocco and Western Sahara print "Western European Standard Time" and "Western European Summer Time" instead of "GMT+00:00" and "GMT+01:00" (Node 24.22.1 does so from 2026-09-20 01:00 UTC, when Morocco returned to permanent UTC);
EST5EDT,CST6CDT,MST7MDTandPST8PDTprint their offset (GMT-05:00) instead of the names of New York, Chicago, Denver and Los Angeles; and the switches between standard and daylight names of Alberta, Manitoba, the Northwest Territories, Colombia (1992) and Iran (1979) follow tz 2026e. Node 22.19 and 24.19 print other names for these zones.
BF-648ff3e-14
The Cloudflare host's pi-durable facade sends Pi v1.4.0's client identity in every model request's options…
- Source: PiG's own defect
- Symptom: The Cloudflare host's pi-durable facade sends Pi v1.4.0's client
identityin every model request's options (last key, aftersessionId), merged overDEFAULT_HARNESS_IDENTITY(provider attribution to pi) withHarnessOptions.identityand a request's ownidentity, as Pi'sHarnessdoes throughwithClientIdentity(Piadc5fa9f,04a93bad). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The Cloudflare host's pi-durable facade sends Pi v1.4.0's client
identityin every model request's options (last key, aftersessionId), merged overDEFAULT_HARNESS_IDENTITY(provider attribution to pi) withHarnessOptions.identityand a request's ownidentity, as Pi'sHarnessdoes throughwithClientIdentity(Piadc5fa9f,04a93bad). The facade exportsDEFAULT_HARNESS_IDENTITY.
BF-648ff3e-15
The Go, Rust and Python extension SDKs' isContextOverflow treats an HTTP 413 error as a context overflow…
- Source: PiG's own defect
- Symptom: The Go, Rust and Python extension SDKs'
isContextOverflowtreats an HTTP 413 error as a context overflow whatever the provider or the body says: a failed message that begins with413(then a space, a colon or the end) or with<provider> API error (413):. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The Go, Rust and Python extension SDKs'
isContextOverflowtreats an HTTP 413 error as a context overflow whatever the provider or the body says: a failed message that begins with413(then a space, a colon or the end) or with<provider> API error (413):. Carried forward from Pi 1.4.0 (packages/ai/src/utils/overflow.ts).
BF-648ff3e-16
The Go, Rust and Python extension SDKs' isRetryableAssistantError retries on a status code (429, 500, 502…
- Source: PiG's own defect
- Symptom: The Go, Rust and Python extension SDKs'
isRetryableAssistantErrorretries on a status code (429, 500, 502, 503, 504, 520, 524) only when it stands alone, not inside a request id or another number, retries onstream disconnected before completion, and does not retry a ChatGPT Business workspace usage-limit error (subscription_sharing_workplace_individual_usage_limit_exceeded). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The Go, Rust and Python extension SDKs'
isRetryableAssistantErrorretries on a status code (429, 500, 502, 503, 504, 520, 524) only when it stands alone, not inside a request id or another number, retries onstream disconnected before completion, and does not retry a ChatGPT Business workspace usage-limit error (subscription_sharing_workplace_individual_usage_limit_exceeded). Carried forward from Pi 6e491cfa, 29057ece and cedefc53.
BF-648ff3e-17
CI: TestNodeCellAddedMembersStayWithinTheirOwnState no longer fails under load
- Source: PiG's own defect
- Symptom: CI:
TestNodeCellAddedMembersStayWithinTheirOwnStateno longer fails under load. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: CI:
TestNodeCellAddedMembersStayWithinTheirOwnStateno longer fails under load. It compared the RSS of a one-member Node cell with the RSS of a five-member cell against 6 MiB per added member; one member's RSS read 131.8 to 152.2 MiB (6.4 to 27.5 MiB below its own peak) in 19 runs, so the figure ranged 0.8 to 7.2 MiB and failed once. The probe now reads the retained V8 heap plus external memory right after the forced collection, which gives 0.20 to 0.25 MiB per added member in every run, and the budget is 2 MiB.
BF-648ff3e-18
CI: a Node extension no longer holds a request the host sends it behind the highlight.js language load
- Source: PiG's own defect
- Symptom: CI: a Node extension no longer holds a request the host sends it behind the highlight.js language load.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: CI: a Node extension no longer holds a request the host sends it behind the highlight.js language load. In TUI mode the runtime loads Pi's complete grammar set after the startup events, and
lib/index.jsevaluates its ~190 grammars in one synchronous pass on the thread that serves requests: 125 ms on an idle machine and more than the five-second renderer inactivity lease (D56) on a starved one, so a Markdown transform, render or mouse event sent meanwhile was abandoned while the extension was healthy and the proxy returned its input (got ""inTestMarkdownContextsAcrossSDKs/subprocess-node-packed). Each grammar module is now required on its own event-loop turn before that pass, which only registers them as in Pi.
BF-648ff3e-19
CI: waitForRenderProxy, the wait of the render proxy and packed padding tests for the extension's answer…
- Source: PiG's own defect
- Symptom: CI:
waitForRenderProxy, the wait of the render proxy and packed padding tests for the extension's answer, is bounded by the hang budgettestbudget.Waitinstead of a fixed one second;TestPackedMessageOutputPadAcrossSDKs/gofailed once with "render proxy condition was not met" on a loaded host (not reproduced since: 0 of 108 runs under 150 busy loops, before and after). - Root cause: CI:
waitForRenderProxy, the wait of the render proxy and packed padding tests for the extension's answer, is bounded by the hang budgettestbudget.Waitinstead of a fixed one second;TestPackedMessageOutputPadAcrossSDKs/gofailed once with "render proxy condition was not met" on a loaded host (not reproduced since: 0 of 108 runs under 150 busy loops, before and after). - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: CI:
waitForRenderProxy, the wait of the render proxy and packed padding tests for the extension's answer, is bounded by the hang budgettestbudget.Waitinstead of a fixed one second;TestPackedMessageOutputPadAcrossSDKs/gofailed once with "render proxy condition was not met" on a loaded host (not reproduced since: 0 of 108 runs under 150 busy loops, before and after).
BF-648ff3e-2
A streamed child-session text costs wire bytes linear in its length: message_update events travel as a…
- Source: PiG's own defect
- Symptom: A streamed child-session text costs wire bytes linear in its length:
message_updateevents travel as a keyframe followed by operations (setandappend) and the SDK hands the listener the full event. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A streamed child-session text costs wire bytes linear in its length:
message_updateevents travel as a keyframe followed by operations (setandappend) and the SDK hands the listener the full event. A 4096-delta, 98 KB reply cost 406 MB on the connection and 826 MB of host allocation per prompt; it now costs 1.8 MB and 19 MB.
BF-648ff3e-20
CI: TestStartupReusesPreTrustExtensionsAcrossEntrypoints no longer reads an extension process that is still…
- Source: PiG's own defect
- Symptom: CI:
TestStartupReusesPreTrustExtensionsAcrossEntrypointsno longer reads an extension process that is still dying as a leak. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: CI:
TestStartupReusesPreTrustExtensionsAcrossEntrypointsno longer reads an extension process that is still dying as a leak. The RPC entrypoint SIGKILLs its extension processes and exits without waiting, and the test polledkill(pid, 0)for five seconds, which also succeeds while the killed process waits for a CPU turn and while it is an unreaped zombie. The test now waits for the process's exit event (pidfd, kqueueNOTE_EXIT, process handle).
BF-648ff3e-21
CI: the scoped-model selector parity scenarios no longer capture the selector before the catalog refresh…
- Source: PiG's own defect
- Symptom: CI: the scoped-model selector parity scenarios no longer capture the selector before the catalog refresh settles.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: CI: the scoped-model selector parity scenarios no longer capture the selector before the catalog refresh settles. Both binaries show "Refreshing model catalogs…" until
refreshModelCatalogssettles and "Model catalogs refreshed." after it.13-scoped-models-fuzzy-filterfinished at "> mtw", and08-scoped-models-selectorand13-scoped-google-catalogwaited a fixed two seconds, so under load a capture could hold either binary's refreshing line (6 of 25 runs of the first and 21 of 50 of the other two failed onmainwith 250 busy loops); each step now waits for the status on the visible screen.
BF-648ff3e-22
CI: a Node extension no longer holds the first request the host sends it behind its bytecode-cache flush
- Source: PiG's own defect
- Symptom: CI: a Node extension no longer holds the first request the host sends it behind its bytecode-cache flush.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: CI: a Node extension no longer holds the first request the host sends it behind its bytecode-cache flush. The runtime flushed Node's compile cache when it processed the host's ready message, on the thread that serves requests; the flush serializes every compiled module and took 4.6 s on a starved machine, so a Markdown transform sent right after registration passed the five-second renderer inactivity lease (D56) while the extension was healthy and the proxy returned its input (
got ""inTestMarkdownContextsAcrossSDKs/subprocess-node-packed). The flush now runs when the factory has loaded, before the member registers.
BF-648ff3e-23
CI: TestTerminalProgramGetsDefaultSignalsWhenTheDaemonStartedIgnoringThem no longer fails every run under…
- Source: PiG's own defect
- Symptom: CI:
TestTerminalProgramGetsDefaultSignalsWhenTheDaemonStartedIgnoringThemno longer fails every run undermake test-fast. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: CI:
TestTerminalProgramGetsDefaultSignalsWhenTheDaemonStartedIgnoringThemno longer fails every run undermake test-fast. It required every ignored-signal bit of the terminal's program to be clear, but Pi'spty.rs(and PiG's daemon, which mirrors it) resets signals 1 through 31 only, and GNU make 4.4.1 starts its recipes with signals 32 and 33 ignored, so the program ended withSigIgn0x80000000 under both daemons. The test asserts signals 1 through 31, the daemon's contract.
BF-648ff3e-24
A package manifest glob walks as Node 24.22 and later walk it
- Source: PiG's own defect
- Symptom: A package manifest glob walks as Node 24.22 and later walk it.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A package manifest glob walks as Node 24.22 and later walk it. Pi runs on that Node, and its
globSyncno longer ends a directory listing at an entry the walk already holds, nor queues the segment after a globstar below a symlink:a/**/../*.mdlists the root's.mdfiles as well,Sub/**/../*lists the siblings ofSub, and**/x/**/ylists everyx/**/ybelow the root.
BF-648ff3e-25
An HTTP 413 from any provider is treated as request-size overflow, so the agent compacts and retries instead…
- Source: PiG's own defect
- Symptom: An HTTP 413 from any provider is treated as request-size overflow, so the agent compacts and retries instead of showing a raw API error.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: An HTTP 413 from any provider is treated as request-size overflow, so the agent compacts and retries instead of showing a raw API error. GitHub Copilot answers a request over its byte-size limit (for example a history of many images) with a 413 and a generic
failed to parse requestbody that the token-count patterns never matched (Pi 73f8de80, #11016).
BF-648ff3e-26
On Windows, PiG finds PowerShell in its installation directories when it is not on PATH: PowerShell 7 under…
- Source: PiG's own defect
- Symptom: On Windows, PiG finds PowerShell in its installation directories when it is not on
PATH: PowerShell 7 under%ProgramFiles%\PowerShell\7afterPATH, and Windows PowerShell under%SystemRoot%\System32\WindowsPowerShell\v1.0as the last fallback, on any drive (Pi 61060ddf, #9490). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: On Windows, PiG finds PowerShell in its installation directories when it is not on
PATH: PowerShell 7 under%ProgramFiles%\PowerShell\7afterPATH, and Windows PowerShell under%SystemRoot%\System32\WindowsPowerShell\v1.0as the last fallback, on any drive (Pi 61060ddf, #9490).
BF-648ff3e-27
On Windows, the legacy WSL bash.exe launcher is recognized under the actual Windows directory in…
- Source: PiG's own defect
- Symptom: On Windows, the legacy WSL
bash.exelauncher is recognized under the actual Windows directory inSystemRoot(System32andSysnative), not only underC:\Windows, so its command goes over stdin (Pi 61060ddf, #9490). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Windows, WSL
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: On Windows, the legacy WSL
bash.exelauncher is recognized under the actual Windows directory inSystemRoot(System32andSysnative), not only underC:\Windows, so its command goes over stdin (Pi 61060ddf, #9490).
BF-648ff3e-28
On Windows, an empty SystemRoot takes C:\Windows for taskkill.exe, as an unset one already did, in the…
- Source: PiG's own defect
- Symptom: On Windows, an empty
SystemRoottakesC:\Windowsfortaskkill.exe, as an unset one already did, in the shell tools and the Durable node environment (Pi 61060ddf, #9490). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: On Windows, an empty
SystemRoottakesC:\Windowsfortaskkill.exe, as an unset one already did, in the shell tools and the Durable node environment (Pi 61060ddf, #9490).
BF-648ff3e-29
On Windows, shells that are runnable Microsoft Store aliases are accepted: a configured shell path…
- Source: PiG's own defect
- Symptom: On Windows, shells that are runnable Microsoft Store aliases are accepted: a configured shell path,
bash.exeandpwsh.exefound bywhere, and the Git Bash locations are checked with an attribute check (access(F_OK)) instead of a stat that follows the alias (Pi 28bea006, nodejs/node#36790). - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: On Windows, shells that are runnable Microsoft Store aliases are accepted: a configured shell path,
bash.exeandpwsh.exefound bywhere, and the Git Bash locations are checked with an attribute check (access(F_OK)) instead of a stat that follows the alias (Pi 28bea006, nodejs/node#36790). Not yet run on a Windows host.
BF-648ff3e-3
coding.CreateAgentSession binds the Session it returns to the Extension Host its resource loader started…
- Source: PiG's own defect
- Symptom:
coding.CreateAgentSessionbinds the Session it returns to the Extension Host its resource loader started, as it does for a child session an extension creates: the loader's extensions read the Session's model, scopedModels, idle state, tools and session, with no UI. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
coding.CreateAgentSessionbinds the Session it returns to the Extension Host its resource loader started, as it does for a child session an extension creates: the loader's extensions read the Session's model, scopedModels, idle state, tools and session, with no UI. The helpers that bind a Session to subprocess extensions in every mode (ExtensionHostSessionActions,BindSessionExtensionActions,BindSessionReadActions,BindSessionModelOperations,WatchSessionRunSignal) moved from the CLI package tocoding, so print, JSON, RPC and interactive mode and a library caller run the same code.
BF-648ff3e-30
URL parsing follows Node 24.22 and later
- Source: PiG's own defect
- Symptom: URL parsing follows Node 24.22 and later.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: URL parsing follows Node 24.22 and later. Pi runs on that Node, whose URL parser only lowercases an ASCII host:
https://xn--.exampleandftp:xn--mailto:are valid,file://xn--a.b/xis a UNC path on Windows, and a provider or Radius base such asfile:///C:ab/corfile:///u:p@[::1]:9000no longer keeps its first path segment as a drive letter when a path is resolved against it.
BF-648ff3e-31
A packed Go, Python or Rust cell no longer hangs when a later member's factory calls the host before an…
- Source: PiG's own defect
- Symptom: A packed Go, Python or Rust cell no longer hangs when a later member's factory calls the host before an earlier member's factory has run.
- Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A packed Go, Python or Rust cell no longer hangs when a later member's factory calls the host before an earlier member's factory has run. The generated runner started every member on a thread of its own and let them race for the factory turn; the host accepts the members in order and serves a connection only after the members before it, so a later member whose factory subscribed to the event bus held the turn and waited for a host that waited for the earlier member. The runner now starts each member's factory in the cell's order.
BF-648ff3e-32
An extension's ctx.hasPendingMessages() now asks the Host instead of reading a cached state snapshot, so it…
- Source: PiG's own defect
- Symptom: An extension's
ctx.hasPendingMessages()now asks the Host instead of reading a cached state snapshot, so it reports a steering or follow-up message queued while a handler is awaited, as Pi's live getter does. - Root cause: Extensions that gate delivery on that flag, such as
pi-queue-steer, no longer reportPi did not accept the queued message at this delivery boundaryafter a message that was delivered. - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: An extension's
ctx.hasPendingMessages()now asks the Host instead of reading a cached state snapshot, so it reports a steering or follow-up message queued while a handler is awaited, as Pi's live getter does. Extensions that gate delivery on that flag, such aspi-queue-steer, no longer reportPi did not accept the queued message at this delivery boundaryafter a message that was delivered.
BF-648ff3e-33
Durable with telemetry off (no telemetry or the no-op span) no longer builds span attributes, events or…
- Source: PiG's own defect
- Symptom: Durable with telemetry off (no telemetry or the no-op span) no longer builds span attributes, events or metric attribute bags, and
ai.Models.Streamwithout a parent span captures nothing for the telemetry observer, so both allocate as before the telemetry integration; Durable's reflective restore also caches each struct type's field list. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Durable with telemetry off (no telemetry or the no-op span) no longer builds span attributes, events or metric attribute bags, and
ai.Models.Streamwithout a parent span captures nothing for the telemetry observer, so both allocate as before the telemetry integration; Durable's reflective restore also caches each struct type's field list. The AI telemetry schema is built on first use instead of at process start, and non-numeric attribute names no longer allocate injsstring.ArrayIndex, which cuts the allocations of a recordedaistream.
BF-648ff3e-34
The grep tool passes --no-config to ripgrep, as Pi does at 8c4e8738, so a user's RIPGREP_CONFIG_PATH…
- Source: PiG's own defect
- Symptom: The grep tool passes
--no-configto ripgrep, as Pi does at8c4e8738, so a user'sRIPGREP_CONFIG_PATHfile cannot change the tool's contract (literal, glob, limit). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The grep tool passes
--no-configto ripgrep, as Pi does at8c4e8738, so a user'sRIPGREP_CONFIG_PATHfile cannot change the tool's contract (literal, glob, limit).
BF-648ff3e-35
Recovery of a truncated assistant attempt (retry and overflow/length compaction) finds the tool results that…
- Source: PiG's own defect
- Symptom: Recovery of a truncated assistant attempt (retry and overflow/length compaction) finds the tool results that answered the attempt's calls in the agent transcript and omits them with it, so a resumed session no longer persists orphaned tool results that providers reject (Pi
02726086, #11227). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Recovery of a truncated assistant attempt (retry and overflow/length compaction) finds the tool results that answered the attempt's calls in the agent transcript and omits them with it, so a resumed session no longer persists orphaned tool results that providers reject (Pi
02726086, #11227).
BF-648ff3e-36
Classifier models take images (Pi 7f225f25): models.classify() accepts images for TypeSafe's Jev and…
- Source: PiG's own defect
- Symptom: Classifier models take images (Pi
7f225f25):models.classify()acceptsimagesfor TypeSafe's Jev and Cloudflare's Clef models, and provider-levelcompatinmodels.jsonnow also applies to classifier models, with the System One fieldsimageInputandsendImageOptInHeader. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Classifier models take images (Pi
7f225f25):models.classify()acceptsimagesfor TypeSafe's Jev and Cloudflare's Clef models, and provider-levelcompatinmodels.jsonnow also applies to classifier models, with the System One fieldsimageInputandsendImageOptInHeader. A model without image input rejects images with a clear error.
BF-648ff3e-37
The catalog tolerates the free field Pi v1.4.0's radius data carries (recursive/flint), keeping it in the…
- Source: PiG's own defect
- Symptom: The catalog tolerates the
freefield Pi v1.4.0's radius data carries (recursive/flint), keeping it in the model record as Pi does. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The catalog tolerates the
freefield Pi v1.4.0's radius data carries (recursive/flint), keeping it in the model record as Pi does.
BF-648ff3e-38
The upstream pin moves to Pi v1.4.0
- Source: PiG's own defect
- Symptom: The upstream pin moves to Pi v1.4.0.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The upstream pin moves to Pi v1.4.0.
BF-648ff3e-39
The durable turn classifier retries on a status code (429, 500, 502, 503, 504, 520, 524) only when it stands…
- Source: PiG's own defect
- Symptom: The durable turn classifier retries on a status code (429, 500, 502, 503, 504, 520, 524) only when it stands alone, not inside a request id or another number, and on
stream disconnected before completion; a ChatGPT Business workspace usage-limit error is not retried. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The durable turn classifier retries on a status code (429, 500, 502, 503, 504, 520, 524) only when it stands alone, not inside a request id or another number, and on
stream disconnected before completion; a ChatGPT Business workspace usage-limit error is not retried. Carried forward from Pi 6e491cfa, 29057ece and cedefc53.
BF-648ff3e-4
Extension tools of one parallel batch reach the host in Pi's order
- Source: PiG's own defect
- Symptom: Extension tools of one parallel batch reach the host in Pi's order.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Extension tools of one parallel batch reach the host in Pi's order. Pi runs a batch on one thread, so the host calls the tools make in their first tick (
appendEntry,ui.notify,ui.setStatus,setActiveTools,sendMessage, and the rest) apply in the order the extension sent them, whichever tool made each; before, eight parallel tools that each called the host applied their calls in scheduler order, and in a Node extension that also reordered the tools' answers, sotool_execution_endand the tool_result hooks came in a different order on every run.
BF-648ff3e-44
Automatic retry no longer treats digits inside a request id or another number (for example req_011CU503xyz…
- Source: PiG's own defect
- Symptom: Automatic retry no longer treats digits inside a request id or another number (for example
req_011CU503xyzor5242880 bytes) as a retryable HTTP status code: 429, 500, 502, 503, 504, 520 and 524 count only as standalone numbers. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Automatic retry no longer treats digits inside a request id or another number (for example
req_011CU503xyzor5242880 bytes) as a retryable HTTP status code: 429, 500, 502, 503, 504, 520 and 524 count only as standalone numbers. Carried forward from Pi 6e491cfa (earendil-works/pi#11057).
BF-648ff3e-45
Automatic retry now triggers for stream disconnected before completion errors that a proxy wrapping the…
- Source: PiG's own defect
- Symptom: Automatic retry now triggers for
stream disconnected before completionerrors that a proxy wrapping the Codex CLI relays. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Automatic retry now triggers for
stream disconnected before completionerrors that a proxy wrapping the Codex CLI relays. Carried forward from Pi 29057ece (earendil-works/pi#11057).
BF-648ff3e-46
Sign in with ChatGPT: a ChatGPT Business workspace usage-limit error…
- Source: PiG's own defect
- Symptom: Sign in with ChatGPT: a ChatGPT Business workspace usage-limit error (
subscription_sharing_workplace_individual_usage_limit_exceeded) is not retried, and its message tells the user to ask their workspace admin to increase the limit. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Sign in with ChatGPT: a ChatGPT Business workspace usage-limit error (
subscription_sharing_workplace_individual_usage_limit_exceeded) is not retried, and its message tells the user to ask their workspace admin to increase the limit. Carried forward from Pi cedefc53.
BF-648ff3e-47
Context and the types that hold one (EventBus, ModelRegistry, ReplacedSessionContext…
- Source: PiG's own defect
- Symptom:
Contextand the types that hold one (EventBus,ModelRegistry,ReplacedSessionContext,SessionManager,SetupSessionManager) in the Go extension SDK are comparable again: the tool-call input aContextcarries (ToolInput,ToolParams) is a pointer to the request's arguments instead of a slice, so adding it is no longer an incompatible change. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
Contextand the types that hold one (EventBus,ModelRegistry,ReplacedSessionContext,SessionManager,SetupSessionManager) in the Go extension SDK are comparable again: the tool-call input aContextcarries (ToolInput,ToolParams) is a pointer to the request's arguments instead of a slice, so adding it is no longer an incompatible change.
BF-648ff3e-48
pi.events.emit in a Go, Rust or Python factory behaves as Pi's does: the first factory-time bus call…
- Source: PiG's own defect
- Symptom: The emit was queued until after ready, and its failure only reached stderr.
- Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
pi.events.emitin a Go, Rust or Python factory behaves as Pi's does: the first factory-time bus call connects to the host (asparseFrontmatterdoes) andRunreuses the connection; a listener the factory subscribes is registered with the host at once and answers the host's dispatches on that connection while the factory waits for a host call, so it hears another extension's emit made while the factory runs, a nested reply to the factory's own emit included, as Pi's shared emitter delivers it; the call returns once the host has delivered the emit to every listener, in registration order (the extensions loaded before this one, then this one's own), and each has finished; a listener that throws or panics is written to stderr asEvent handler error (channel):and reaches neither the emitter nor the next listener; anerroremit that nobody hears fails with the emitted error. Before, the emit was queued until after ready, and its failure only reached stderr. A factory with no host (noPIG_EXT_SOCKET) gets an error from its first bus call. Go gainssdk.ServeFactory(conn, factory)for a fused binary, which gives the factory the pipe as its connection.
BF-648ff3e-49
The Python SDK test test_provider_transport_shutdown_does_not_settle_owned_stream[True-*] no longer fails…
- Source: PiG's own defect
- Symptom: The Python SDK test
test_provider_transport_shutdown_does_not_settle_owned_stream[True-*]no longer fails when the worker thread is still returning at the moment the shutdown finds no thread to wait for: it waits for the worker, as the assertion means "ends", not "has already ended". - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The Python SDK test
test_provider_transport_shutdown_does_not_settle_owned_stream[True-*]no longer fails when the worker thread is still returning at the moment the shutdown finds no thread to wait for: it waits for the worker, as the assertion means "ends", not "has already ended".
BF-648ff3e-5
The tool_result handlers of a parallel batch start in the order the tools settled, each running to its…
- Source: PiG's own defect
- Symptom: The
tool_resulthandlers of a parallel batch start in the order the tools settled, each running to its first await before the next one starts, and a tool'sonUpdatereaches the listeners in the order the tools called it, across tools; before, both followed goroutine wake order. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The
tool_resulthandlers of a parallel batch start in the order the tools settled, each running to its first await before the next one starts, and a tool'sonUpdatereaches the listeners in the order the tools called it, across tools; before, both followed goroutine wake order.
BF-648ff3e-50
A Go extension SDK loop that is served without Run (a test on its own connection) no longer waits for a…
- Source: PiG's own defect
- Symptom: A Go extension SDK loop that is served without
Run(a test on its own connection) no longer waits for a loaded gate that never closes;TestToolRequestAllocBudgethung for the whole test timeout. - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A Go extension SDK loop that is served without
Run(a test on its own connection) no longer waits for a loaded gate that never closes;TestToolRequestAllocBudgethung for the whole test timeout.
BF-648ff3e-51
The Node extension API probe of a failed factory (TestUpstreamFailedFactoryDisablesCapturedAPI) names…
- Source: PiG's own defect
- Symptom: The Node extension API probe of a failed factory (
TestUpstreamFailedFactoryDisablesCapturedAPI) namesonLoaded, which the runtime already rejects after a failed load; the test failed with an uncaught assertion sinceonLoadedwas added. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The Node extension API probe of a failed factory (
TestUpstreamFailedFactoryDisablesCapturedAPI) namesonLoaded, which the runtime already rejects after a failed load; the test failed with an uncaught assertion sinceonLoadedwas added.
BF-648ff3e-52
A listener a Go, Rust or Python factory subscribes hears another extension's emit made while the factory…
- Source: PiG's own defect
- Symptom: The listener was registered when
Runconnected and missed such an emit. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A listener a Go, Rust or Python factory subscribes hears another extension's emit made while the factory runs, including a nested reply to the factory's own emit, as Pi's shared emitter delivers it: the listener is registered with the host when it is subscribed, a factory emit goes only through the host's ordered registry, and the factory's connection answers the host's dispatches while the factory waits for a host call. Before, the listener was registered when
Runconnected and missed such an emit.
BF-648ff3e-53
A command that an extension started with pi.exec and an AbortSignal kept running after the signal…
- Source: PiG's own defect
- Symptom: A command that an extension started with
pi.execand anAbortSignalkept running after the signal aborted: the signal never reached the host in the Go, Rust and Python SDKs, and the Node runtime sent it as an empty object. - Root cause: A command that an extension started with
pi.execand anAbortSignalkept running after the signal aborted: the signal never reached the host in the Go, Rust and Python SDKs, and the Node runtime sent it as an empty object. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A command that an extension started with
pi.execand anAbortSignalkept running after the signal aborted: the signal never reached the host in the Go, Rust and Python SDKs, and the Node runtime sent it as an empty object. In print, JSON and RPC mode the command did not even end with the request, because the host ran it under a context that nothing cancels.
BF-648ff3e-54
Extension API parity ledger: removed the stale ctx.ui.custom(options.onHandle) exception (Go, Rust and…
- Source: PiG's own defect
- Symptom: Extension API parity ledger: removed the stale
ctx.ui.custom(options.onHandle)exception (Go, Rust and Python implement the overlay handle;TestCustomOverlayHandleSDKsMatchruns it on every SDK), reworded the keybinding-hint, highlight andcreateAgentSessionrows that said "Node runtime only", and added a "Native package-export gaps" table naming the closing lane for every export Node serves and the native SDKs lack. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Extension API parity ledger: removed the stale
ctx.ui.custom(options.onHandle)exception (Go, Rust and Python implement the overlay handle;TestCustomOverlayHandleSDKsMatchruns it on every SDK), reworded the keybinding-hint, highlight andcreateAgentSessionrows that said "Node runtime only", and added a "Native package-export gaps" table naming the closing lane for every export Node serves and the native SDKs lack.
BF-648ff3e-55
Conformance fixtures now reach the realized SDK export cells the export ledger listed as unreached: the…
- Source: PiG's own defect
- Symptom: Conformance fixtures now reach the realized SDK export cells the export ledger listed as unreached: the editor component's
insert_text_at_cursor/add_to_historyoverrides (Go, Rust, Python), the GoEditorqueries and setters,DEFAULT_MAX_LINES/DEFAULT_MAX_BYTES, the session manager'sisPersisted,usesDefaultSessionDir,getChildrenandbuildSessionContext, Rust's typedserialize_conversationandstrip_frontmatterin Python, each compared with Pi's own answer on the Node runtime or the host's. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Conformance fixtures now reach the realized SDK export cells the export ledger listed as unreached: the editor component's
insert_text_at_cursor/add_to_historyoverrides (Go, Rust, Python), the GoEditorqueries and setters,DEFAULT_MAX_LINES/DEFAULT_MAX_BYTES, the session manager'sisPersisted,usesDefaultSessionDir,getChildrenandbuildSessionContext, Rust's typedserialize_conversationandstrip_frontmatterin Python, each compared with Pi's own answer on the Node runtime or the host's.pi-tuiparseColor(Python) andstyleTextWithAnsi(Python, Rust) move fromnativetogap:SdkTuiHelpers: the Pythonparse_colorvalidates a wire color, not Pi'sparseColor, and the Rust function is crate-private.
BF-648ff3e-56
Releasing a handle of an object a child session was created over (a ModelRuntime first of all) no longer…
- Source: PiG's own defect
- Symptom: An extension that built a runtime, created a child over it and let the runtime go out of scope had the runtime's model tasks stopped while the child still ran on it.
- Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Releasing a handle of an object a child session was created over (a
ModelRuntimefirst of all) no longer closes the object under the session. The host counts the holders of every session graph object, the extension's handle and each child session created over it, and releases the object when the last one lets go. Before, an extension that built a runtime, created a child over it and let the runtime go out of scope had the runtime's model tasks stopped while the child still ran on it.
BF-648ff3e-57
Removing a local package reports start and complete remove progress events, as Pi's remove does for…
- Source: PiG's own defect
- Symptom: Removing a local package reports
startandcompleteremoveprogress events, as Pi'sremovedoes for every source kind. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Removing a local package reports
startandcompleteremoveprogress events, as Pi'sremovedoes for every source kind.
BF-648ff3e-58
registerProvider of a model without an api fails with Pi's Set it at model level or provider level.…
- Source: PiG's own defect
- Symptom:
registerProviderof a model without anapifails with Pi'sSet it at model level or provider level.wording. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
registerProviderof a model without anapifails with Pi'sSet it at model level or provider level.wording.
BF-648ff3e-59
An extension's SessionManager.appendContextEdit(id, null) clears the content instead of failing, and…
- Source: PiG's own defect
- Symptom: An extension's
SessionManager.appendContextEdit(id, null)clears the content instead of failing, andappendCompactionwithoutfromHookwrites nofromHookmember. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: An extension's
SessionManager.appendContextEdit(id, null)clears the content instead of failing, andappendCompactionwithoutfromHookwrites nofromHookmember.
BF-648ff3e-6
The Go, Python and Rust SDKs start the handlers of concurrent event and command requests in the order the…
- Source: PiG's own defect
- Symptom: The Go, Python and Rust SDKs start the handlers of concurrent event and command requests in the order the requests arrive, as they already did for
tool_call; before, twotool_resulthooks in flight on one connection could start their handlers out of order (the Go SDK did so in 25 of 3000 probes). - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The Go, Python and Rust SDKs start the handlers of concurrent event and command requests in the order the requests arrive, as they already did for
tool_call; before, twotool_resulthooks in flight on one connection could start their handlers out of order (the Go SDK did so in 25 of 3000 probes).
BF-648ff3e-60
Extension SDKs: formatKeyText/keyDisplayText upper-cased the first rune of a part, where Pi's…
- Source: PiG's own defect
- Symptom: Extension SDKs:
formatKeyText/keyDisplayTextupper-cased the first rune of a part, where Pi'scharAt(0).toUpperCase()leaves a character beyond the Basic Multilingual Plane (half a surrogate pair) as it is. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Extension SDKs:
formatKeyText/keyDisplayTextupper-cased the first rune of a part, where Pi'scharAt(0).toUpperCase()leaves a character beyond the Basic Multilingual Plane (half a surrogate pair) as it is.
BF-648ff3e-61
Extensions: ctx.ui.setTheme(theme) with a theme object makes that object the active theme as it is, as Pi's…
- Source: PiG's own defect
- Symptom: Extensions:
ctx.ui.setTheme(theme)with a theme object makes that object the active theme as it is, as Pi'ssetThemeInstancedoes. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Extensions:
ctx.ui.setTheme(theme)with a theme object makes that object the active theme as it is, as Pi'ssetThemeInstancedoes. PiG re-created the object in the terminal's color depth, so a truecolor theme set on a terminal without truecolor became a 256-color copy (and the other way round).
BF-648ff3e-62
A PiG TLS client no longer accepts a certificate chain through a compromised eMudhra intermediate or a root…
- Source: PiG's own defect
- Symptom: A PiG TLS client no longer accepts a certificate chain through a compromised eMudhra intermediate or a root Mozilla removed after it; the error names the CA.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A PiG TLS client no longer accepts a certificate chain through a compromised eMudhra intermediate or a root Mozilla removed after it; the error names the CA.
BF-648ff3e-63
The CI images no longer ship the three removed emSign roots: ca-certificates 20261020-r0 in the ci-go lock…
- Source: PiG's own defect
- Symptom: The CI images no longer ship the three removed emSign roots:
ca-certificates20261020-r0 in the ci-go lock, 20260909-r3 and a newer Wolfi base (glibc 2.44) in the ci-parity lock, which also takesc-ares1.34.9 andnodejs-2424.22.1. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The CI images no longer ship the three removed emSign roots:
ca-certificates20261020-r0 in the ci-go lock, 20260909-r3 and a newer Wolfi base (glibc 2.44) in the ci-parity lock, which also takesc-ares1.34.9 andnodejs-2424.22.1.
BF-648ff3e-64
Shutting a host down while a child session is live no longer leaves its extension host running
- Source: PiG's own defect
- Symptom: Shutting a host down while a child session is live no longer leaves its extension host running.
- Root cause: A session an extension created with
createAgentSessionoutlives the extension'sdispose()of it, and aDefaultResourceLoaderan extension built outlives the extension's handle while a session uses it; the host closed both only when the extension's connection closed, on a goroutineShutdowndid not wait for, and never stopped the loader's extension host. - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Shutting a host down while a child session is live no longer leaves its extension host running. A session an extension created with
createAgentSessionoutlives the extension'sdispose()of it, and aDefaultResourceLoaderan extension built outlives the extension's handle while a session uses it; the host closed both only when the extension's connection closed, on a goroutineShutdowndid not wait for, and never stopped the loader's extension host.Shutdownnow closes every live child session and releases every object it held before it returns, and a released loader stops the extension host it started.
BF-648ff3e-65
Extensions: in a packed Go, Rust or Python cell, a member that stops or is replaced no longer releases a file…
- Source: PiG's own defect
- Symptom: Extensions: in a packed Go, Rust or Python cell, a member that stops or is replaced no longer releases a file mutation turn (
withFileMutationQueue) that another member's task still holds. - Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Extensions: in a packed Go, Rust or Python cell, a member that stops or is replaced no longer releases a file mutation turn (
withFileMutationQueue) that another member's task still holds. The turns of a packed process now belong to the process: they are released byfileMutation.exitover any of its connections, or when its last connection closes.
BF-648ff3e-66
Extensions: withFileMutationQueue is now one queue for the built-in edit and write tools and every…
- Source: PiG's own defect
- Symptom: Each extension process kept its own queue, so a Go, Rust, Python or Node extension tool and a built-in tool on one file, or two extensions, could lose an update.
- Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Extensions:
withFileMutationQueueis now one queue for the built-ineditandwritetools and every extension process, as in Pi. Before, each extension process kept its own queue, so a Go, Rust, Python or Node extension tool and a built-in tool on one file, or two extensions, could lose an update. The SDKs ask the host's queue for the file's turn (fileMutation.enter,fileMutation.exit); the turn is released if the extension's connection closes.
BF-648ff3e-67
A Python extension's ctx.session_name and a Rust extension's ctx.session_name() returned an empty string…
- Source: PiG's own defect
- Symptom: A Python extension's
ctx.session_nameand a Rust extension'sctx.session_name()returned an empty string for the life of the process, because nothing ever read the name from the state the host sends. - Root cause: A Python extension's
ctx.session_nameand a Rust extension'sctx.session_name()returned an empty string for the life of the process, because nothing ever read the name from the state the host sends. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A Python extension's
ctx.session_nameand a Rust extension'sctx.session_name()returned an empty string for the life of the process, because nothing ever read the name from the state the host sends. Every SDK now keeps the session name current from each state update, and a Go extension has the same accessor,Context.SessionName.
BF-648ff3e-68
ctx.model is the model's id in every SDK, as in Pi (ctx.model.id)
- Source: PiG's own defect
- Symptom:
ctx.modelis the model's id in every SDK, as in Pi (ctx.model.id). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note:
ctx.modelis the model's id in every SDK, as in Pi (ctx.model.id). A Python extension'sctx.modeland a Rust extension'sctx.model()returned the model's display name (Claude Sonnet 4.5), so a comparison withclaude-sonnet-4-5or aprovider/modelstring built from it failed there and passed in Go. Python and Rust also havemodel_providerandmodel_qualified, as Go hasModelProviderandModelQualified.
BF-648ff3e-69
A read of the session name, the session log or the model right after setLabel, setSessionName…
- Source: PiG's own defect
- Symptom: A read of the session name, the session log or the model right after
setLabel,setSessionName,sendMessageorsetModelcould return the old value in a Go, Python or Rust extension. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A read of the session name, the session log or the model right after
setLabel,setSessionName,sendMessageorsetModelcould return the old value in a Go, Python or Rust extension. The host now sends its state before the result of those calls, and the Go SDK waits for it, so the next read sees the write as it does in Pi.
BF-648ff3e-7
An edit or write call of a parallel batch registers in the file mutation queue after the extension tools…
- Source: PiG's own defect
- Symptom: The built-in call registered in the dispatcher's loop, a process hop before the extension's registration arrived, so
[extension tool, write]applied the write first. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: An edit or write call of a parallel batch registers in the file mutation queue after the extension tools before it in the batch took the queue, as Pi does: Pi registers a call in the queue when it calls
withFileMutationQueueand runs each call's first statements in call order. Before, the built-in call registered in the dispatcher's loop, a process hop before the extension's registration arrived, so[extension tool, write]applied the write first. The Go, Python, Rust and Node SDKs, isolated, packed and fused, now agree. A native tool that waits on something other than a host call before it takes the queue still registers first, and a write behind a native tool that makes no host call waits for the tool; both are recorded in the parity row.
BF-648ff3e-70
An extension that reads ctx.model, pi.getSettings(), pi.getSessionName(), pi.getThinkingLevel() or…
- Source: PiG's own defect
- Symptom: An extension that reads
ctx.model,pi.getSettings(),pi.getSessionName(),pi.getThinkingLevel()or its footer's git branch and provider count from a timer, thread or goroutine between requests now sees a change the host makes itself. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: An extension that reads
ctx.model,pi.getSettings(),pi.getSessionName(),pi.getThinkingLevel()or its footer's git branch and provider count from a timer, thread or goroutine between requests now sees a change the host makes itself. A model switch (Ctrl+P,/model, cycling, RPCset_model), a settings edit,/nameor RPCset_session_name, a resumed session, a branch checkout and a login updated the host but reached an extension only at its next request, so a background reader held the old value; Pi reads the live session. The host now pushes the new state to every connected extension when it changes, off the UI loop, and sends nothing to an extension that already holds it.
BF-648ff3e-71
In a Rust extension's before_agent_start handler, ctx.get_system_prompt() returns the prompt the…
- Source: PiG's own defect
- Symptom: In a Rust extension's
before_agent_starthandler,ctx.get_system_prompt()returns the prompt the handler's own edit ofsystemPromptOptionsrenders, as Pi'sctx.getSystemPrompt()and the Go, Python and Node SDKs do. - Root cause: It returned the prompt rendered before the edit, because the handler held the event exclusively and the context could not read it.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: In a Rust extension's
before_agent_starthandler,ctx.get_system_prompt()returns the prompt the handler's own edit ofsystemPromptOptionsrenders, as Pi'sctx.getSystemPrompt()and the Go, Python and Node SDKs do. It returned the prompt rendered before the edit, because the handler held the event exclusively and the context could not read it. The handler now edits the options through theBeforeAgentStartEventthe context shares, and a read after an edit asks the host to render the edited options.
BF-648ff3e-72
RPC mode no longer writes entry_appended for a pi.appendEntry or session_info_changed for a…
- Source: PiG's own defect
- Symptom: RPC mode no longer writes
entry_appendedfor api.appendEntryorsession_info_changedfor api.setSessionNamethat an extension'ssession_starthandler makes, and--mode jsonno longer writes anything asession_starthandler produced: itsentry_appended,session_info_changed, and themessage_startandmessage_endof asendMessage. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: RPC mode no longer writes
entry_appendedfor api.appendEntryorsession_info_changedfor api.setSessionNamethat an extension'ssession_starthandler makes, and--mode jsonno longer writes anything asession_starthandler produced: itsentry_appended,session_info_changed, and themessage_startandmessage_endof asendMessage. Pi subscribes its stdout writer after the extensions are bound, so none of these reach a client; the JSON stream now starts at the prompt, after the session header. Thesession_startof a replacement command (new_session,switch_session,fork) that runs after the writer exists is still written, as in Pi.
BF-648ff3e-73
Docs: another PiG build that shares the config root no longer removes the documentation bundle a running…
- Source: PiG's own defect
- Symptom: Docs: another PiG build that shares the config root no longer removes the documentation bundle a running session's system prompt names.
- Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Docs: another PiG build that shares the config root no longer removes the documentation bundle a running session's system prompt names. A session leases its bundle until it exits; before, a session that outlived four newer builds' use lost its pages and nothing wrote them again.
BF-648ff3e-74
RPC mode now writes the members of an extension_ui_request that Pi writes empty, as Pi's JSON.stringify…
- Source: PiG's own defect
- Symptom: In the Go, Python and Rust SDKs
SetStatus(key, "")is now an empty status text (it used to remove the entry), andClearStatus/clear_statusis Pi'ssetStatus(key, undefined); an empty placeholder or prefill is the unset member (Python:None). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: RPC mode now writes the members of an
extension_ui_requestthat Pi writes empty, as Pi'sJSON.stringifydoes:message: ""ofnotify,title: ""ofsetTitle,text: ""ofset_editor_text(how a client clears its editor),statusText: ""ofsetStatusand an emptyoptionslist ofselectwere dropped, so a client could not tell empty from absent.inputandeditorno longer writeplaceholder: ""/prefill: ""when the extension passed none. In the Go, Python and Rust SDKsSetStatus(key, "")is now an empty status text (it used to remove the entry), andClearStatus/clear_statusis Pi'ssetStatus(key, undefined); an empty placeholder or prefill is the unset member (Python:None). The interactive footer is unchanged.
BF-648ff3e-75
Starting a second Piglet Binary instance no longer fails on Windows while the first is running
- Source: PiG's own defect
- Symptom: Starting a second Piglet Binary instance no longer fails on Windows while the first is running.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Starting a second Piglet Binary instance no longer fails on Windows while the first is running. Extraction left the embedded Go, Rust and Python cell binaries rewritten at every start, and Windows refuses to replace the executable of a running process (
Access is denied); a cell binary that already holds the embedded bytes is now left in place, and only a different one is replaced, with a retry on a transient lock.
BF-648ff3e-76
RPC mode writes one setWidget request per ctx.ui.setWidget call from the Go, Python and Rust SDKs, in…
- Source: PiG's own defect
- Symptom: RPC mode writes one
setWidgetrequest perctx.ui.setWidgetcall from the Go, Python and Rust SDKs, in call order and before the command's response, as Pi does. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: RPC mode writes one
setWidgetrequest perctx.ui.setWidgetcall from the Go, Python and Rust SDKs, in call order and before the command's response, as Pi does. A string list with no options went out as an unanswered frame that the host applied on a lane of its own: the last list a command set could be written after the command's response or after a laternotify, a repeat of the same list was dropped, and a list set after a clear stayed cleared on the client. In RPC mode the SDKs now send the list as the host call every other widget shape uses.
BF-648ff3e-77
In print and JSON mode, a /skill:name prompt whose skill file cannot be read now reports the skill's read…
- Source: PiG's own defect
- Symptom: In print and JSON mode, a
/skill:nameprompt whose skill file cannot be read now reports the skill's read error (Extension error (<SKILL.md path>): ENOENT: ...) before the missing-credentials error, as Pi does. - Root cause: Not recorded.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: In print and JSON mode, a
/skill:nameprompt whose skill file cannot be read now reports the skill's read error (Extension error (<SKILL.md path>): ENOENT: ...) before the missing-credentials error, as Pi does. Skill commands and prompt templates are expanded before the selected model's authentication is checked; they were expanded after it, so the skill error never appeared when the provider had no credentials.
BF-648ff3e-78
Images are now resized with the image profile of the model that answered last under a virtual model selection
- Source: PiG's own defect
- Symptom: Images are now resized with the image profile of the model that answered last under a virtual model selection.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Images are now resized with the image profile of the model that answered last under a virtual model selection. Prompt images and tool-result images used the selected virtual model, which declares no image limits, so a routed model's
inputLimits.images.resizenever applied. Pi sizes both with the physical model of the latest routed response, and the selected model otherwise.
BF-648ff3e-79
The system prompt a run forced no longer shows while the bash and custom messages held during that run are…
- Source: PiG's own defect
- Symptom: The system prompt a run forced no longer shows while the bash and custom messages held during that run are flushed at its end.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The system prompt a run forced no longer shows while the bash and custom messages held during that run are flushed at its end. A listener of a flushed custom message that read the system prompt saw the run's forced prompt, where Pi ends the run's prompt options first and reports the base prompt.
BF-648ff3e-8
The Node runtime reports a tool whose execute awaits something that is not a host call, and the host closes…
- Source: PiG's own defect
- Symptom: The Node runtime reports a tool whose execute awaits something that is not a host call, and the host closes the tool's start window after the calls the extension sent before the report, not after the call its handler sends next.
- Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The Node runtime reports a tool whose execute awaits something that is not a host call, and the host closes the tool's start window after the calls the extension sent before the report, not after the call its handler sends next.
BF-648ff3e-80
An extension that reads pi.getMcpServers() from a timer, thread or goroutine and has no…
- Source: PiG's own defect
- Symptom: An extension that reads
pi.getMcpServers()from a timer, thread or goroutine and has nomcp_servers_changehandler now sees a server another extension registers or removes. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: An extension that reads
pi.getMcpServers()from a timer, thread or goroutine and has nomcp_servers_changehandler now sees a server another extension registers or removes. The host updated the registry but reached an extension only at its next request, so the reader kept the old list; Pi reads the registry live. The host now pushes the new list to every connected extension when the registry changes, off the call that changed it, and sends nothing to an extension that already holds it.
BF-648ff3e-81
An extension that reads ctx.ui.theme from a timer, thread or goroutine now sees the theme the user selects…
- Source: PiG's own defect
- Symptom: An extension that reads
ctx.ui.themefrom a timer, thread or goroutine now sees the theme the user selects (/settings, a theme file edit, the terminal's reported colors). - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: An extension that reads
ctx.ui.themefrom a timer, thread or goroutine now sees the theme the user selects (/settings, a theme file edit, the terminal's reported colors). The host replaced the active theme but sent the palette only with the extension's next request; Pi'sctx.ui.themeis the global theme, read at every use. The host now pushes the new palette to every connected extension when the theme changes.
BF-648ff3e-82
A Node extension that registers or unregisters a virtual model…
- Source: PiG's own defect
- Symptom: A Node extension that registers or unregisters a virtual model (
pi.registerVirtualModel/unregisterVirtualModel) sees the write on the next line, as Pi does:ctx.modelRegistry.find,getAllandgetAvailablelist the model, a provider of only virtual models is configured and available at once, and an unregistration removes the model. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A Node extension that registers or unregisters a virtual model (
pi.registerVirtualModel/unregisterVirtualModel) sees the write on the next line, as Pi does:ctx.modelRegistry.find,getAllandgetAvailablelist the model, a provider of only virtual models is configured and available at once, and an unregistration removes the model. The host also publishes the catalog when it serves either call, so another extension's registry sees the model.
BF-648ff3e-83
CI: the nightly -race run now covers the packages that own most of PiG's goroutines
- Source: PiG's own defect
- Symptom: CI: the nightly
-racerun now covers the packages that own most of PiG's goroutines. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: CI: the nightly
-racerun now covers the packages that own most of PiG's goroutines.coding,coding/cli(run as four shards),coding/extension, the extension hosts (inproc,runtimecell,cellpack),internal/packagemanager,internal/pilock,internal/pigsdklock,coding/mcpext,coding/rpcclient, theextensions/sdkGo module,internal/codingagent/toolsand the concurrency libraries were in no-racejob before.make race-coverage(part ofcheck-coreandci-drift) fails when a package that starts goroutines or timers and has tests is in no nightly group ofautomation/ci/race-packages.txtand not exempt with a reason. The first-racerun of all of them found no data race.
BF-648ff3e-84
A pig session on another machine or container that shares your ~/.pig (an NFS home, a bind mount) is no…
- Source: PiG's own defect
- Symptom: A pig session on another machine or container that shares your
~/.pig(an NFS home, a bind mount) is no longer reported as crashed by the next pig you start. - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A pig session on another machine or container that shares your
~/.pig(an NFS home, a bind mount) is no longer reported as crashed by the next pig you start. Session markers incrash-output/record the host and PID namespace, and a marker from another one is left alone instead of being removed with a false "ended without recording an exit" line.
BF-648ff3e-85
The upstream test inventory records Pi's shell-path-exists.test.ts as ported instead of partial: Pi mocks…
- Source: PiG's own defect
- Symptom: The upstream test inventory records Pi's
shell-path-exists.test.tsas ported instead of partial: Pi mocksaccessSyncandspawnSync, PiG's Windows alias and Unix cases match them case for case, and the native Windows check ran and passed on the Windows runner. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The upstream test inventory records Pi's
shell-path-exists.test.tsas ported instead of partial: Pi mocksaccessSyncandspawnSync, PiG's Windows alias and Unix cases match them case for case, and the native Windows check ran and passed on the Windows runner. The release gate no longer reports it as an open hot-path gap.
BF-648ff3e-86
Fixed a chord host root failing with superseded: code version N is not retained although it was started in…
- Source: PiG's own defect
- Symptom: Fixed a chord host root failing with
superseded: code version N is not retainedalthough it was started in the current code version: the host picked the current code version and counted the root in it in two separate steps, and a reload's retirement of that code version could fall between them. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Fixed a chord host root failing with
superseded: code version N is not retainedalthough it was started in the current code version: the host picked the current code version and counted the root in it in two separate steps, and a reload's retirement of that code version could fall between them. The root now starts in one step, as upstream's does.
BF-648ff3e-87
Print mode and --mode json no longer run an extension command a second time on text an input handler or a…
- Source: PiG's own defect
- Symptom: Print mode and
--mode jsonno longer run an extension command a second time on text an input handler or a prompt template produced. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Print mode and
--mode jsonno longer run an extension command a second time on text an input handler or a prompt template produced. A handler that rewrote the prompt into an extension command's invocation, or a template whose body is that invocation, ran the command and sent nothing to the model. Pi checks for a command on the raw text only and sends the produced text to the model.
BF-648ff3e-88
Shutting down a host while a crashed extension waits out its restart backoff no longer leaves a goroutine…
- Source: PiG's own defect
- Symptom: Shutting down a host while a crashed extension waits out its restart backoff no longer leaves a goroutine sleeping for up to 30 seconds.
- Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Shutting down a host while a crashed extension waits out its restart backoff no longer leaves a goroutine sleeping for up to 30 seconds. Shutdown ends the wait and waits for it, so embedders and tests see no goroutine after the host is shut down.
BF-648ff3e-89
Forking a session from a file no longer overwrites a session file that already has the new session's…
- Source: PiG's own defect
- Symptom: Forking a session from a file no longer overwrites a session file that already has the new session's timestamp and id.
- Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Forking a session from a file no longer overwrites a session file that already has the new session's timestamp and id. The fork now fails with
EEXIST: file already existsand leaves the file untouched, as Pi does, instead of truncating it.
BF-648ff3e-9
A Node extension tool that awaits the event loop before it takes the file mutation queue registers after the…
- Source: PiG's own defect
- Symptom: A Node extension tool that awaits the event loop before it takes the file mutation queue registers after the edit or write call behind it, as in Pi; before, the two raced and the extension won every round.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A Node extension tool that awaits the event loop before it takes the file mutation queue registers after the edit or write call behind it, as in Pi; before, the two raced and the extension won every round. The runtime holds the tool's next
fileMutation.enteruntil the host reports the writes behind the tool registered.
BF-648ff3e-90
Extension SDKs: ModelRegistry.find reports a host or transport failure instead of "no such model"
- Source: PiG's own defect
- Symptom: Extension SDKs:
ModelRegistry.findreports a host or transport failure instead of "no such model". - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Extension SDKs:
ModelRegistry.findreports a host or transport failure instead of "no such model". The GoFindreturns(model, error)and the Rustfindreturnsio::Result<Option<_>>; Python already raised. A host with nogetModelorsessionReadbinding answers an error, so a list-typed session read is never a null.
BF-648ff3e-91
Python SDK: has_configured_auth and is_using_oauth answer False for a model with no provider, as Pi and…
- Source: PiG's own defect
- Symptom: Python SDK:
has_configured_authandis_using_oauthanswerFalsefor a model with no provider, as Pi and the Go, Rust and Node SDKs do, instead of raisingKeyError. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Python SDK:
has_configured_authandis_using_oauthanswerFalsefor a model with no provider, as Pi and the Go, Rust and Node SDKs do, instead of raisingKeyError.
BF-648ff3e-92
Go SDK: Context.CancellationReason returns the reason the host gave when it cancelled the request, as the…
- Source: PiG's own defect
- Symptom: Go SDK:
Context.CancellationReasonreturns the reason the host gave when it cancelled the request, as the Python and Rust SDKs do. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Go SDK:
Context.CancellationReasonreturns the reason the host gave when it cancelled the request, as the Python and Rust SDKs do.
BF-648ff3e-93
A compiled-in extension factory, inline or built in, no longer panics "Extension runtime not initialized"…
- Source: PiG's own defect
- Symptom: A compiled-in extension factory, inline or built in, no longer panics "Extension runtime not initialized" when it calls
pi.sendMessage,appendEntry,setSessionName,getSessionName,setModel,setThinkingLevelorgetThinkingLevelunder the interactive mode. - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A compiled-in extension factory, inline or built in, no longer panics "Extension runtime not initialized" when it calls
pi.sendMessage,appendEntry,setSessionName,getSessionName,setModel,setThinkingLevelorgetThinkingLevelunder the interactive mode. The mode rebinds its in-process runner after the Session binds it and replaced the Session's actions with three of them; it now binds every action Pi's_bindExtensionCorebinds, through the code the wire host actions of the same names run.
BF-648ff3e-94
The interactive mode no longer rewrites every provider request's system prompt into one head
- Source: PiG's own defect
- Symptom: The interactive mode no longer rewrites every provider request's system prompt into one head.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: The interactive mode no longer rewrites every provider request's system prompt into one head. It set the agent's system prompt for every run and again after every tool change and reload, so a mid-session change to the prompt or the active tools reached the model as a rewritten head with the transcript's system patch messages dropped. Pi sends the prompt as the transcript's sections and patch messages and forces text onto the request only for the
systemPromptabefore_agent_starthandler returned; the interactive mode now does the same.
BF-648ff3e-95
Markdown bold and italic render next to CJK punctuation in the terminal, for example…
- Source: PiG's own defect
- Symptom: Markdown bold and italic render next to CJK punctuation in the terminal, for example
**这是测试。**后面(Pi #10730). - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Markdown bold and italic render next to CJK punctuation in the terminal, for example
**这是测试。**后面(Pi #10730). Text without CJK keeps the CommonMark flanking rules.
BF-648ff3e-96
A message sent with deliverAs: "nextTurn" while a run was starting could join that run instead of the next…
- Source: PiG's own defect
- Symptom: A message sent with
deliverAs: "nextTurn"while a run was starting could join that run instead of the next one. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: A message sent with
deliverAs: "nextTurn"while a run was starting could join that run instead of the next one. A run takes the pending nextTurn messages when its preparation ends, before the bash results and custom messages held while it started are appended, as Pi does. The Session took them after that, when it claimed the Agent, so a Session event listener that answered a held custom message'smessage_endwith a nextTurn message (a Go program embedding the Session; no extension handler sees those events) had it reach the model a run early.
BF-648ff3e-97
Markdown bold and italic next to CJK punctuation render in HTML exports and in Markdown that Node extensions…
- Source: PiG's own defect
- Symptom: Markdown bold and italic next to CJK punctuation render in HTML exports and in Markdown that Node extensions render through pi-tui, matching the terminal (Pi #11168, #10730).
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Markdown bold and italic next to CJK punctuation render in HTML exports and in Markdown that Node extensions render through pi-tui, matching the terminal (Pi #11168, #10730).
BF-648ff3e-98
An extension's UI closures now run only in the TUI, as in Pi
- Source: PiG's own defect
- Symptom: An extension's UI closures now run only in the TUI, as in Pi.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: An extension's UI closures now run only in the TUI, as in Pi. In RPC mode the Node runtime no longer runs
setFooter/setHeaderfactories (a throwing render leaked anotifyerror), keepssetEditorComponentandonTerminalInputhandlers, forwards the working-indicator calls, answersgetEditorComponentwith the installed factory, or failssetTheme(Theme); they are no-ops,getEditorComponentanswers undefined andsetThemeanswers{success:false,error:"Theme switching not supported in RPC mode"}. The Go, Python and Rust SDKs no longer run footer and header renderers in RPC, print or JSON mode, andCustomin RPC mode resolves nil/None without running the component.
BF-648ff3e-99
Interactive mode now routes a message an extension sends with sendMessage while a run streams as Pi does
- Source: PiG's own defect
- Symptom: A message with no
deliverAssteers: the run delivers it after the current tool batch, before the next model call, where it used to wait until the run would stop. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
648ff3e2b0(2026-10-22, #288) - Tests: None recorded.
- Release note: Interactive mode now routes a message an extension sends with
sendMessagewhile a run streams as Pi does. A message with nodeliverAssteers: the run delivers it after the current tool batch, before the next model call, where it used to wait until the run would stop. A message withtriggerTurn: falseis held until the turn's tool results are complete and appended without a model call, where it used to be queued as a follow-up that cost the run another model call.sendMessageno longer rejects an emptycustomTypeor an unknowndeliverAs(Pi accepts the first and steers the second).
BF-65426f3
fix(rpc): pipelined set_models apply in input order, after the batch's synchronous responses
- Source: PiG's own defect
- Symptom: fix(rpc): pipelined set_models apply in input order, after the batch's synchronous responses
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
65426f3ab3(2026-10-10) - Tests:
coding/cli/rpc_model_type_binary_test.go::TestRPCModelObjectsCarryTheRegistryModelType,coding/cli/rpc_resume_integration_test.go::TestRPCModeSessionReplacementAndExport,coding/cli/rpc_set_model_await_test.go::TestRPCSetModelAppliesAfterTheBatchesLaterCommands,coding/cli/rpc_set_model_pipeline_test.go::TestRPCPipelinedSetModelsApplyInInputOrder
BF-65932e6
fix(extensions): run a tick call after the tool_execution_end of the tool call that queued its callback…
- Source: PiG's own defect
- Symptom: fix(extensions): run a tick call after the tool_execution_end of the tool call that queued its callback, whichever reaches the host first
- Root cause: A timer or immediate callback a Node tool queued while a sibling tool still awaits runs, in Pi, right after the first tool's tool_execution_end. The runtime is another process: it writes the callback's host call a few hundred microseconds after the tool's answer, and the host passed the tick boundary of that tool in about the same time. A call that arrived after the boundary was held for the next tool_execution_end of any tool, so scenario 69 (rpc-sibling-tool-timer-call-order) wrote the immediate and timeout0 notifications behind the sibling's own notification in 8 of 20 single-pair runs on ordering/d5-timer-calls alone and 4 of 20 on the train.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
65932e6efb(2026-10-21) - Tests:
coding/extension/host/subprocess/callpayload_shape_test.go::TestCallPayloadKeepsUnkeyedLiteralShape,coding/extension/host/subprocess/timer_turns_test.go::TestNodeTickCallsOfAToolRunAfterItsEndWhenTheyArriveLate,coding/extension/host/subprocess/timer_turns_test.go::TestNodeTimerCallbackCallsWaitForTheLoopTurn,internal/jsloop/turns_test.go::TestTurnsForgetATickBoundaryWhenTheToolCallStartsAgain,internal/jsloop/turns_test.go::TestTurnsRunATickAfterItsToolCallsEndWhicheverArrivesFirst,internal/jsloop/turns_test.go::TestTurnsRunTicksAtTheTickBoundaryBeforeTimers,test/extension-conformance/timer_calls_test.go::TestNodeTimerCallbackNotificationsWaitForTheEndOfTheRun
BF-65932e6-2
A notification, status or message that an extension queued with setImmediate or setTimeout from a tool…
- Source: PiG's own defect
- Symptom: A notification, status or message that an extension queued with
setImmediateorsetTimeoutfrom a tool that returned while a sibling tool in the same batch still awaited reached an RPC client behind the sibling's own output in up to two runs in five, where Pi writes it right after the first tool'stool_execution_end. - Root cause: A timer or immediate callback a Node tool queued while a sibling tool still awaits runs, in Pi, right after the first tool's tool_execution_end. The runtime is another process: it writes the callback's host call a few hundred microseconds after the tool's answer, and the host passed the tick boundary of that tool in about the same time. A call that arrived after the boundary was held for the next tool_execution_end of any tool, so scenario 69 (rpc-sibling-tool-timer-call-order) wrote the immediate and timeout0 notifications behind the sibling's own notification in 8 of 20 single-pair runs on ordering/d5-timer-calls alone and 4 of 20 on the train.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
4ac21c86c4(2026-10-21),65932e6efb(2026-10-21) - Tests:
coding/extension/host/subprocess/callpayload_shape_test.go::TestCallPayloadKeepsUnkeyedLiteralShape,coding/extension/host/subprocess/timer_turns_test.go::TestNodeTickCallsOfAToolRunAfterItsEndWhenTheyArriveLate,coding/extension/host/subprocess/timer_turns_test.go::TestNodeTimerCallbackCallsWaitForTheLoopTurn,internal/jsloop/turns_test.go::TestTurnsForgetATickBoundaryWhenTheToolCallStartsAgain,internal/jsloop/turns_test.go::TestTurnsRunALateTickAtOnceWhileNoWaitIsOpen,internal/jsloop/turns_test.go::TestTurnsRunATickAfterItsToolCallsEndWhicheverArrivesFirst,internal/jsloop/turns_test.go::TestTurnsRunTicksAtTheTickBoundaryBeforeTimers,test/extension-conformance/timer_calls_test.go::TestNodeTimerCallbackNotificationsWaitForTheEndOfTheRun - Release note: A notification, status or message that an extension queued with
setImmediateorsetTimeoutfrom a tool that returned while a sibling tool in the same batch still awaited reached an RPC client behind the sibling's own output in up to two runs in five, where Pi writes it right after the first tool'stool_execution_end. The Node runtime is another process: it wrote the callback's host call a few hundred microseconds after the tool's answer, and the host passed that tool'stool_execution_endin about the same time, so a call that arrived after it waited for the nexttool_execution_endof any tool. The call now says which tool call queued its callback (origin_tool_call_id). The host runs it behind that call'stool_execution_endwhen it arrives before the event, and at once when it arrives after, where the runtime says a sibling tool still runs and the loop turns.
BF-663e84b
The built-in models page lists the real thinking levels (off, minimal, low, medium, high, xhigh, max) instead…
- Source: PiG's own defect
- Symptom: The built-in models page lists the real thinking levels (off, minimal, low, medium, high, xhigh, max) instead of a five-step ladder that started at none.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
663e84b2d8(2026-10-14) - Tests: None recorded.
- Release note: The built-in models page lists the real thinking levels (off, minimal, low, medium, high, xhigh, max) instead of a five-step ladder that started at none. Reported by @SkrOYC (#194).
BF-663e84b-2
The built-in models page documents the models.json provider and model fields (models[], contextWindow…
- Source: PiG's own defect
- Symptom: The built-in models page documents the
models.jsonprovider and model fields (models[],contextWindow,maxTokens,modelOverrides) with an example, plus provider cost tiers, local models and troubleshooting. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
663e84b2d8(2026-10-14) - Tests: None recorded.
- Release note: The built-in models page documents the
models.jsonprovider and model fields (models[],contextWindow,maxTokens,modelOverrides) with an example, plus provider cost tiers, local models and troubleshooting. Reported by @SkrOYC (#193).
BF-663e84b-3
The built-in docs gain the compaction summary format, branch summaries and extension hooks, the RPC, JSON…
- Source: PiG's own defect
- Symptom: The built-in docs gain the compaction summary format, branch summaries and extension hooks, the RPC, JSON mode, CLI integration, session format, message types, llama.cpp, security and usage pages, and "PiG" replaces "Pig" throughout.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
663e84b2d8(2026-10-14) - Tests: None recorded.
- Release note: The built-in docs gain the compaction summary format, branch summaries and extension hooks, the RPC, JSON mode, CLI integration, session format, message types, llama.cpp, security and usage pages, and "PiG" replaces "Pig" throughout. The platform pages stay on the site only, by choice. Reported by @SkrOYC (#195).
BF-663e84b-4
The packages page says how to build and run pig-acp, which npm:@pi-in-go/pigpen-acp needs but does not…
- Source: PiG's own defect
- Symptom: The packages page says how to build and run
pig-acp, whichnpm:@pi-in-go/pigpen-acpneeds but does not install, and how to point Zed at it. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
663e84b2d8(2026-10-14) - Tests: None recorded.
- Release note: The packages page says how to build and run
pig-acp, whichnpm:@pi-in-go/pigpen-acpneeds but does not install, and how to point Zed at it. Reported by @klonuo (#210).
BF-6646adc
A durable transaction that settles a submission with a detail that is not strict JSON now counts as having…
- Source: PiG's own defect
- Symptom: A durable transaction that settles a submission with a detail that is not strict JSON now counts as having written a table, as Pi's
settleSubmissiondoes: later table reads in the same commit are refused, and on a settled transaction the settled error is reported before the JSON check. - Root cause: Differences found: every message used Go's %q where Pi interpolates the name between plain double quotes (constrained-sampling.ts:136-150,188,226,259,267), so a tool named 'we"ird' got backslashes; isStructuredSchema tested properties/items with != nil although JS tests !== undefined (an explicit null counts) and searched a type array through toStringSlice, which gave up when any element was not a string where Array.includes does not. Mutants killed: each of those, the delta no-op and closed-buffer rules, the JS trim of grammar variants, the nullable-wrap and required checks. Go cannot hold a non-object tool.parameters, so boolean/array/string roots are not probed (type-level difference).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
172a6e3cd9(2026-10-10),6646adc3b2(2026-10-10),749840b14d(2026-10-10) - Tests:
ai/constrained_sampling_oracle_test.go::TestConstrainedSamplingMatchesPi,durable/entry_edits_presence_test.go::TestEntryEditsKeepAPresentEmptyList,durable/session/entry_edits_copy_test.go::TestCopyEntryRecordKeepsAPresentEmptyEditList,durable/session/settle_submission_order_test.go::TestSettleSubmissionChecksOpenAndMarksTheWriteBeforeCopying,durable/storage/jsonl_pi_oracle_test.go::TestJsonlStorageMatchesPiOnSeededPrograms,durable/storage/memory_pi_oracle_test.go::TestMemoryStorageMatchesPiOnSeededPrograms,durable/storage/session_pi_oracle_test.go::TestSessionTransactionsMatchPiOnSeededPrograms - Release note: A durable transaction that settles a submission with a detail that is not strict JSON now counts as having written a table, as Pi's
settleSubmissiondoes: later table reads in the same commit are refused, and on a settled transaction the settled error is reported before the JSON check.
BF-666acff
Package filters, manifest !/+/- overrides, settings resource patterns and built-in extension overrides…
- Source: PiG's own defect
- Symptom: Package filters, manifest
!/+/-overrides, settings resource patterns and built-in extension overrides follow minimatch 10.2.6 and its brace-expansion 5.0.12 limits, as in Pi. - Root cause: 17 fail a test; the other lets the last combine's shortcut ignore dropEmpties, which is equivalent because the values loop already drops those empties. The port still equals Pi's copy on the 200,000-pattern corpus.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
49798a80d2(2026-10-07),a79de03e7f(2026-10-07),1cbb6b6fb2(2026-10-09),4401fa4630(2026-10-09),4cc5ef904b(2026-10-09),6584435bb6(2026-10-09),6e6b9132c2(2026-10-09),926f5d2e63(2026-10-09),aff3d7266f(2026-10-09),d586d71f85(2026-10-09),ef007a4e5e(2026-10-09),6410ef49f6(2026-10-10),666acff687(2026-10-10),6edaafdfe2(2026-10-10),6f8d83861a(2026-10-10),c1055c449e(2026-10-10),fce99d43d7(2026-10-10) - Tests:
coding/cli/cli_package_glob_test.go::TestCLIGitSourceRefreshesOncePerLoad,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceGlobThrowFirstSourceWins,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceGlobThrowFollowsPi,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceInstallFailureStopsStartup,coding/cli/cli_package_glob_test.go::TestReloadSnapshotReportsCLIPackageGlobThrow,coding/cli/cli_package_glob_test.go::TestStartupStopsAtACLIPackageGlobThrow,coding/cli/settings_file_url_upstream_test.go::TestNpmPackageManifestGlobThrowRejectsResolution,coding/cli/settings_file_url_upstream_test.go::TestSettingsPatternErrorsFollowPiand 58 more in docs/bug-ledger.json - Release note: Package filters, manifest
!/+/-overrides, settings resource patterns and built-in extension overrides follow minimatch 10.2.6 and its brace-expansion 5.0.12 limits, as in Pi.
BF-666acff-2
A pattern that Pi cannot compile now fails where Pi fails: a local Package keeps only the resource kinds…
- Source: PiG's own defect
- Symptom: A pattern that Pi cannot compile now fails where Pi fails: a local Package keeps only the resource kinds collected before it, an npm or git Package (configured or
-e) stops startup and/reloadwith the JavaScript error, and so does a settings pattern or an override on auto-discovered resources. - Root cause: 17 fail a test; the other lets the last combine's shortcut ignore dropEmpties, which is equivalent because the values loop already drops those empties. The port still equals Pi's copy on the 200,000-pattern corpus.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
311609427b(2026-10-07),49798a80d2(2026-10-07),630f56def0(2026-10-07),66bb7180b3(2026-10-07),947d0669ee(2026-10-07),965ef796b1(2026-10-07),a79de03e7f(2026-10-07),a7b6786111(2026-10-07),f235c0bcc5(2026-10-07),1cbb6b6fb2(2026-10-09),4401fa4630(2026-10-09),4cc5ef904b(2026-10-09),6584435bb6(2026-10-09),6e6b9132c2(2026-10-09),926f5d2e63(2026-10-09),9fa02166f5(2026-10-09),aff3d7266f(2026-10-09),d586d71f85(2026-10-09),ef007a4e5e(2026-10-09),6410ef49f6(2026-10-10),666acff687(2026-10-10),6edaafdfe2(2026-10-10),6f8d83861a(2026-10-10),c1055c449e(2026-10-10),fce99d43d7(2026-10-10) - Tests:
ai/simple_options_test.go::TestBuildBaseOptions,coding/cli/cli_package_glob_test.go::TestCLIGitSourceRefreshesOncePerLoad,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceGlobThrowFirstSourceWins,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceGlobThrowFollowsPi,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceInstallFailureStopsStartup,coding/cli/cli_package_glob_test.go::TestReloadSnapshotReportsCLIPackageGlobThrow,coding/cli/cli_package_glob_test.go::TestStartupStopsAtACLIPackageGlobThrow,coding/cli/resource_loader_gaps_upstream_test.go::TestPrintModeInvalidDiscoveredFileURLFailsBindand 72 more in docs/bug-ledger.json - Release note: A pattern that Pi cannot compile now fails where Pi fails: a local Package keeps only the resource kinds collected before it, an npm or git Package (configured or
-e) stops startup and/reloadwith the JavaScript error, and so does a settings pattern or an override on auto-discovered resources.
BF-668b3fa
Fix the shared attribute-order store case: advance the clock before flush and compare metric row names only
- Source: PiG's own defect
- Symptom: Fix the shared attribute-order store case: advance the clock before flush and compare metric row names only
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
668b3fa13b(2026-10-19) - Tests:
telemetry/store_upstream_test.go::TestInMemoryMetricRowsKeepAttributeInsertionOrder
BF-66b0fc0
The Anthropic workload-identity token endpoint check parses the base URL with the WHATWG parser, as the…
- Source: PiG's own defect
- Symptom: The Anthropic workload-identity token endpoint check parses the base URL with the WHATWG parser, as the Anthropic SDK does: loopback hosts reached with userinfo, mixed case or unusual spellings are accepted, and a base URL containing a backslash, quote or non-ASCII text appears unescaped in the error message.
- Root cause: getStandardBedrockEndpointRegion (cut from Pi's bedrock-converse-stream.ts:1223, testdata/bedrock_region.mjs) runs on 3006 seeded base URLs (regions, fips, cn partition, mixed case, full-width letters, tabs/newlines, userinfo, ports, schemes, schemeless and non-URL text). net/url differed on every URL with a tab/newline inside or a full-width/uppercase host (the WHATWG parser strips and folds them) and returned a region for a schemeless '//host' that new URL rejects (red before). bedrockEndpointRegion now reads nodeurl.ParseURL's hostname. Survivor: dropping the empty-string guard, which is equivalent because ParseURL rejects ''.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0b4e1e3376(2026-10-09),15a5c0b670(2026-10-09),2c04c1445a(2026-10-09),31f4517fbe(2026-10-09),66b0fc0539(2026-10-09),7c4d874438(2026-10-09),90882933f6(2026-10-09),a739b6dcf2(2026-10-09),c4d51d5c1a(2026-10-09),d1b3ad0151(2026-10-09),d1cbde3321(2026-10-09),d80c1cd150(2026-10-09),da8a8704c6(2026-10-09) - Tests:
ai/anthropic_token_endpoint_oracle_test.go::TestAnthropicTokenEndpointRuleMatchesSDK,ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,ai/azure_config_oracle_test.go::TestAzureBaseURLNormalizationMatchesPi,ai/bedrock_region_oracle_test.go::TestBedrockEndpointRegionMatchesPi,ai/chatgpt_manual_input_oracle_test.go::TestChatGPTManualInputMatchesPiOnSeededProbes,ai/codex_account_id_oracle_test.go::TestCodexAccountIDMatchesPi,ai/codex_vertex_urls_oracle_test.go::TestCodexWebSocketAndVertexVersionURLsMatchPi,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPiand 6 more in docs/bug-ledger.json - Release note: The Anthropic workload-identity token endpoint check parses the base URL with the WHATWG parser, as the Anthropic SDK does: loopback hosts reached with userinfo, mixed case or unusual spellings are accepted, and a base URL containing a backslash, quote or non-ASCII text appears unescaped in the error message.
BF-66db311
The login dialog now holds TUI focus while it is shown, so the prompt cursor appears in the right place for…
- Source: PiG's own defect
- Symptom: The login dialog now holds TUI focus while it is shown, so the prompt cursor appears in the right place for IME input.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
66db311977(2026-10-06) - Tests:
tui/key_helper_test.go::TestKeyBuildersMatchInput,tui/key_helper_test.go::TestKeyMatchesUpstreamKeyObject,tui/login_dialog_pi_test.go::TestLoginDialogFocusPropagatesToPromptInput,tui/login_dialog_pi_test.go::TestLoginDialogMaskDisabledMatchesPi,tui/theme_tokens_test.go::TestThemeColorsAreKeyedByEveryThemeToken - Release note: The login dialog now holds TUI focus while it is shown, so the prompt cursor appears in the right place for IME input.
BF-6708fbb
When a Go, Rust or Python extension that shares a process registers under a different name than the one it…
- Source: PiG's own defect
- Symptom: It said only
packed extension registered as "<name>". - Root cause: A Go, Rust or Python member of a packed process whose factory registers a name other than its selected identity failed with only 'packed extension registered as "<name>"'. The isolated path already names both identities, the source, and how to fix the selection. Both paths now build that error with registeredIdentityMismatch.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0631c73a3f(2026-10-09),6708fbb379(2026-10-09),b751326e80(2026-10-09) - Tests:
coding/extension/host/subprocess/liveness_test.go::TestPackedMemberTransportClosureDoesNotProveProcessDeath,coding/extension/host/subprocess/packed_go_test.go::TestHost_KillingPackedProcessNeverClaimsItRemainedAlive,coding/extension/host/subprocess/packed_go_test.go::TestHost_LoadGoPackedCellRejectsRegisteredIdentityMismatchActionably,extensions/sdk-py/tests/test_overlay_width.py,extensions/sdk/overlay_width_test.go::TestRemoteRenderWidthFollowsUpstreamOverlayLayout,test/extension-conformance/overlay_width_conformance_test.go::TestConformance_OverlayRenderWidth - Release note: When a Go, Rust or Python extension that shares a process registers under a different name than the one it was selected by, the error now names both identities, the source, and how to fix the selection, as it already did for an extension in its own process. Before, it said only
packed extension registered as "<name>".
BF-677ded1
An indented line after a quoted heading, rule or fence in a blockquote renders as the code block Pi shows
- Source: PiG's own defect
- Symptom: An indented line after a quoted heading, rule or fence in a blockquote renders as the code block Pi shows.
- Root cause: marked joins an indented lazy line to a quoted paragraph with its four spaces removed only when the quoted line before it is text; after a heading, rule or fence it is a code block of its own. Pig stripped the spaces whatever the quoted line was. The strip now requires markdownIsTextLine of the last quoted line.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
677ded1860(2026-10-11) - Tests:
tui/markdown_quote_lazy_oracle_test.go::TestMarkdownQuoteIndentedLazyLineAfterABlockMatchesPi - Release note: An indented line after a quoted heading, rule or fence in a blockquote renders as the code block Pi shows.
BF-68a4ed2
PageUp and PageDown move the selection by a page in selection lists and selectors (settings, model…
- Source: PiG's own defect
- Symptom: PageUp and PageDown move the selection by a page in selection lists and selectors (settings, model, extension, session, tree, trust, login and the open editor autocomplete) and no longer scroll the fullscreen transcript behind them (Pi #7629, PR #7680).
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
68a4ed2b54(2026-10-09) - Tests:
internal/codingagent/fullscreen_modal_viewport_test.go::TestFullscreenSelectionKeepsItsPageKeys,internal/codingagent/fullscreen_modal_viewport_test.go::TestRegularModeSelectorPageKeyMovesItsSelection,internal/codingagent/llama/ui_test.go::TestHuggingFaceSearchPageKeysMoveTenResults,internal/codingagent/modal_viewport_input_test.go::TestEditorSlotSelectorPageKeyMovesSelectionInEveryMode,internal/codingagent/select_page_keys_test.go::TestCustomEditorGivesSelectionPageKeysToAnOpenAutocomplete,internal/codingagent/select_page_keys_test.go::TestFirstTimeSetupPageKeysMoveToTheEnds,internal/codingagent/select_page_keys_test.go::TestSessionSelectorCapturesSelectionPageKeys,internal/codingagent/select_page_keys_test.go::TestTrustSelectorPageKeysMoveToTheEndsand 8 more in docs/bug-ledger.json - Release note: PageUp and PageDown move the selection by a page in selection lists and selectors (settings, model, extension, session, tree, trust, login and the open editor autocomplete) and no longer scroll the fullscreen transcript behind them (Pi #7629, PR #7680).
BF-68a63f2
A tool namespace registered with aliases: [] keeps the empty array in pi.getAllTools() and the replicated…
- Source: PiG's own defect
- Symptom: A tool namespace registered with
aliases: []keeps the empty array inpi.getAllTools()and the replicated tool list, as Pi returns the authored namespace object. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
68a63f2459(2026-10-13) - Tests:
coding/extension/host/subprocess/state_test.go::TestGetAllToolsKeepsANamespacesEmptyAliases - Release note: A tool namespace registered with
aliases: []keeps the empty array inpi.getAllTools()and the replicated tool list, as Pi returns the authored namespace object.extension.ToolNamespace.Aliasesomits only a nil slice.
BF-68b129d
/export to a directory that cannot be created on a read-only file system (such as macOS's root) reports…
- Source: PiG's own defect
- Symptom:
/exportto a directory that cannot be created on a read-only file system (such as macOS's root) reportsENOENT: no such file or directory, mkdir ...as Pi does, notEROFS. - Root cause: node_file.cc MKDirpSync returns EACCES, ENOSPC, ENOTDIR, EPERM and ENOENT from mkdir as they are; for any other error it stats the directory and reports that error. A directory missing from macOS's read-only root is therefore ENOENT in Node (and Pi's /export), where MkdirAll said EROFS. TestExportSessionToJsonlFailuresAreNodes now takes the expected message from Node's mkdirSync on the running OS (EACCES on Linux, ENOENT on macOS).
- User impact: An operation failed or was rejected
- Platform: macOS
- Fixed in: 0.5.0
- Fix commits:
68b129d018(2026-10-13) - Tests:
internal/codingagent/session_export_test.go::TestExportSessionToJsonlFailuresAreNodes,internal/nodeerrno/fserror_test.go::TestMkdirAllAndFromPathErrorMatchNode - Release note:
/exportto a directory that cannot be created on a read-only file system (such as macOS's root) reportsENOENT: no such file or directory, mkdir ...as Pi does, notEROFS.
BF-68ceee2
Tool-argument validation compares JSON values as JSON Schema does: a number is never equal to a string
- Source: PiG's own defect
- Symptom: Tool-argument validation compares JSON values as JSON Schema does: a number is never equal to a string.
- Root cause: The vendored santhosh-tekuri equals() parsed the right-hand side of a numeric left-hand side with big.Rat.SetString, which accepts "1.5", "0x10", "1e3" and "1/2". A schema {"const":"1"} accepted the number 1 (Pi's typebox rejects it), {"const":{"x":"1"}} accepted {"x":1}, and uniqueItems rejected ["1", 1] or ["0x10", 16] (Pi accepts them) when the string came first.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
32e33923d5(2026-10-11),68ceee26fc(2026-10-11) - Tests:
ai/validate_equality_oracle_test.go::TestValidateToolArgumentsComparesNumbersAndStringsLikePi,ai/validate_wrapped_order_lifetime_test.go::TestValidateToolArgumentsDoesNotKeepWrappedObjectsInTheCachedSchema,third_party/jsonschema/equals_number_test.go::TestEqualsDoesNotEquateANumberWithAString - Release note: Tool-argument validation compares JSON values as JSON Schema does: a number is never equal to a string.
const,enumanduniqueItemsno longer treat1,1.5,16or0.5as equal to"1","1.5","0x10"or"1/2"(the schema library parsed the string as a number), matching Pi's validator.
BF-68d0628
The Go, Rust and Python SDKs export Pi's getLanguageFromPath
- Source: PiG's own defect
- Symptom: The Go, Rust and Python SDKs export Pi's
getLanguageFromPath. - Root cause: TestConformance_LanguageFromPathAgreesAcrossSDKs runs every table key in four spellings plus 25 edge paths (no extension, trailing dot, a dot in a directory, spaces, non-ASCII) in every SDK, isolated and packed, against the Node SDK's answer from Pi's own code: all agree. 12 mutants (a table entry, lower-casing, first versus last dot, a dropped entry) are killed in Go, Rust and Python; the empty-extension guard is equivalent because the table has no empty key. A JavaScript object lookup returns an inherited function for 'constructor' or 'toString'; the other SDKs return none, recorded in the parity row.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
68d062839c(2026-10-11) - Tests:
test/extension-conformance/language_from_path_test.go::TestConformance_LanguageFromPathAgreesAcrossSDKs - Release note: The Go, Rust and Python SDKs export Pi's
getLanguageFromPath.
BF-68f8761
insecure: true on a provider an extension registers with refreshModels now reaches the request transport
- Source: PiG's own defect
- Symptom:
insecure: trueon a provider an extension registers withrefreshModelsnow reaches the request transport. - Root cause: Pi's registerProvider merges a re-registration's defined values over the previous registration and keeps the undefined ones (model-runtime.ts:921-933). Go's zero value is the omitted value, so insecure now follows the rule its neighbouring fields already follow: a re-registration that sets it turns it on, one that omits it keeps the earlier opt-in.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0e05901b4c(2026-10-09),cf648c7b8b(2026-10-09),68f87619b5(2026-10-10) - Tests:
coding/extension/host/subprocess/host_test.go::TestHost_BuildExtension_Tools,coding/extension/host/subprocess/host_test.go::TestProtocol_RegisterRoundTrip,coding/extension/host/subprocess/registration_fields_wiring_test.go::TestRegistrationDeclarationsKeepEveryField,coding/provider_refresh_insecure_test.go::TestRefreshModelsRegistrationKeepsInsecure,internal/codingagent/model_registry_test.go::TestModelRegistry_RegisterProvider_InsecureMergesAsPiMergesItsNeighbours - Release note:
insecure: trueon a provider an extension registers withrefreshModelsnow reaches the request transport. A re-registration that omitsinsecurekeeps an earlier opt-in, as Pi keeps every field a re-registration leaves undefined; before, a provider registered withoutrefreshModelsignored aninsecure: truere-registration.
BF-68f98d7
Fixed the error message of a failed lazy API setup, such as a module load or auth failure, using its failure…
- Source: PiG's own defect
- Symptom: Fixed the error message of a failed lazy API setup, such as a module load or auth failure, using its failure time as
timestampinstead of the request start. - Root cause: event-stream.ts AssistantMessageEventStream.#time: the final message of a response gets durationMs measured from the stream's creation on a monotonic clock, unless the message has one or predates the stream (a deferred result fetched later). Plumbed through agent messages, session/RPC JSON (after the provider's keys, before thinkingLevel) and extension message decoding. lazy.ts: a failed setup keeps the request start as its timestamp.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
68f98d7133(2026-10-07) - Tests:
agent/agent_loop_upstream_test.go::TestAgentLoop_TranscriptAssistantMessageCarriesResponseDuration,agent/message_json_test.go::TestAgentMessageJSONAssistantDurationMsOrder,ai/event_stream_timing_test.go::TestAssistantMessageEventStreamTimingUpstream,ai/google_sdk_pipeline_test.go::TestGoogleProviderConsumerMatchesNodeTickOrder,ai/lazy_api_upstream_test.go::TestLazyAPIDeferredCapabilitiesUpstream,internal/codingagent/rebind_ui_upstream_test.go::TestRebindResetsProgramStatus - Release note: Fixed the error message of a failed lazy API setup, such as a module load or auth failure, using its failure time as
timestampinstead of the request start.
BF-6992ffb
A Go, Python or Rust extension that calls appendEntry in a handler that already read the session log now…
- Source: PiG's own defect
- Symptom: It saw the entry only on its first read of the log or after the next host request.
- Root cause: The handler's getEntries() is that SDK's first session read, so the subscription fetched the whole log and counted the entry however appends were replicated: with the Go SDK's applySessionUpdate made a no-op the go case still passed.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8d990c5d54(2026-10-07),31cde9f009(2026-10-09),6992ffba71(2026-10-09),7489735c3f(2026-10-09),f20cc081d5(2026-10-09) - Tests:
coding/cli/rpc_append_entry_sdks_test.go::TestRPCAppendEntryIsVisibleToTheSameHandlerInEverySDK,coding/cli/rpc_append_entry_visible_test.go::TestRPCNodeAppendEntryIsVisibleToTheSameHandlerAndEmitsEntryAppended,coding/extension/host/inproc/upstream_scoped_models_test.go::TestContextThinkingLevelReadsTheBoundRuntimeLive,coding/extension/host/subprocess/builder_node_entry_test.go::TestResolveNodeEntrypointImportsAnIndexMjsDirectoryAsJiti,test/extension-conformance/api_append_entry_test.go::TestConformance_ExtensionAPIAppendEntry,test/parity/runner/snapshot_test.go::TestSnapshotExtensionPathKeepsADirectoryExtensionsName,test/parity/scenarios/extensions-runtime/75-robust-notes-go.toml,test/parity/scenarios/extensions-runtime/75-robust-notes-node.tomland 2 more in docs/bug-ledger.json - Release note: A Go, Python or Rust extension that calls
appendEntryin a handler that already read the session log now sees the new entry in its nextgetEntries(), as in Pi. Before, it saw the entry only on its first read of the log or after the next host request.
BF-69b9811
Two PiG builds that share a config root (two installed versions, or stock PiG beside a Piglet Binary) no…
- Source: PiG's own defect
- Symptom: Two PiG builds that share a config root (two installed versions, or stock PiG beside a Piglet Binary) no longer rewrite each other's documentation at every start.
- Root cause: A sync keeps the four most recently used bundles.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
395c46cbf2(2026-10-09),69b9811164(2026-10-09),852652e3ae(2026-10-09),da99854d1b(2026-10-09) - Tests:
coding/cli/piglet_verify_cache_startup_test.go::TestSignedPigletBinaryRunsWhenItsVerifyCacheCannotBeWritten,coding/cli/readonly_config_root_binary_test.go::TestReadOnlyConfigRootStartsQuietlyAndNamesOnlyDocsThatExist,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,internal/codingagent/auth_guidance_test.go::TestNoTextNamesADocsBundleStartupCouldNotWrite,internal/configroot/configroot_test.go::TestDocsDirIsThePigdocsBundleUnderTheConfigRoot,internal/contentdirs/contentdirs_test.go::TestEvictKeepsTheMostRecentlyUsedAndTheCallersOwn,internal/contentdirs/contentdirs_test.go::TestNameRequiresAHexHash,internal/contentdirs/contentdirs_test.go::TestTouchRecordsUseAtMostHourlyand 2 more in docs/bug-ledger.json - Release note: Two PiG builds that share a config root (two installed versions, or stock PiG beside a Piglet Binary) no longer rewrite each other's documentation at every start. Each build writes its docs to its own directory,
~/.pig/docs/bundle-<hash>/, and the system prompt, login help and codemode name that directory;pig docs pathprints it. A sync keeps the four most recently used bundles.
BF-69c175b
fix(models): models.json entries keep exposed limits and cost; bound thinking-only messages in summaries; pin…
- Source: PiG's own defect
- Symptom: fix(models): models.json entries keep exposed limits and cost; bound thinking-only messages in summaries; pin #8810 (D140, D141)
- Root cause: Pi #9566, Pi #9602 (PR #9717). Pi #8810 does not reproduce in PiG (40 x 3 startup orders x 10 runs); the test pins it.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
69c175bf60(2026-10-09) - Tests:
coding/model_default_provider_race_test.go::TestExtensionProviderIsConfiguredWhenTheDefaultModelIsResolved
BF-69f6341
The Radius relay WebSocket URL is built with the WHATWG URL parser, as Pi's new URL does: gateway hosts are…
- Source: PiG's own defect
- Symptom: The Radius relay WebSocket URL is built with the WHATWG URL parser, as Pi's
new URLdoes: gateway hosts are normalized, userinfo and ports follow Pi, an unparseable gateway reports "Invalid URL", and an unsupported scheme is reported with its trailing colon. - Root cause: The URL expression of requestMistralStream (mistral-conversations.ts:303-305: new URL(baseUrl); pathname set to the trimmed path plus '/'; new URL('v1/chat/completions', baseUrl)) runs in Node (testdata/mistral_url.mjs) on 4514 seeded base URLs. It is now ParseURL, the pathname setter (WithPathname) and ResolveRelativePath; the hand-rolled authority normalizer and resolveDirectoryPath are deleted. The file drive-letter rule cannot trigger here because the directory path always ends in '/'.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0b4e1e3376(2026-10-09),15a5c0b670(2026-10-09),1ee7dea3e4(2026-10-09),31f4517fbe(2026-10-09),66b0fc0539(2026-10-09),69f63414c1(2026-10-09),7c4d874438(2026-10-09),90882933f6(2026-10-09),97e178f7f6(2026-10-09),a739b6dcf2(2026-10-09),d1b3ad0151(2026-10-09),d1cbde3321(2026-10-09),d80c1cd150(2026-10-09),da8a8704c6(2026-10-09) - Tests:
ai/anthropic_token_endpoint_oracle_test.go::TestAnthropicTokenEndpointRuleMatchesSDK,ai/azure_config_oracle_test.go::TestAzureBaseURLNormalizationMatchesPi,ai/bedrock_region_oracle_test.go::TestBedrockEndpointRegionMatchesPi,ai/chatgpt_manual_input_oracle_test.go::TestChatGPTManualInputMatchesPiOnSeededProbes,ai/codex_vertex_urls_oracle_test.go::TestCodexWebSocketAndVertexVersionURLsMatchPi,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,ai/mistral_url_oracle_test.go::TestMistralChatCompletionsURLMatchesPiOnSeededProbes,ai/radius_config_oracle_test.go::TestRadiusConfigMatchesPiOnSeededProbesand 6 more in docs/bug-ledger.json - Release note: The Radius relay WebSocket URL is built with the WHATWG URL parser, as Pi's
new URLdoes: gateway hosts are normalized, userinfo and ports follow Pi, an unparseable gateway reports "Invalid URL", and an unsupported scheme is reported with its trailing colon.
BF-6a6fffa
A session message whose integer member, such as timestamp, a usage count or durationMs, is spelled with a…
- Source: PiG's own defect
- Symptom: PiG dropped the message from the session context and RPC
get_messages. - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,test/longsession/session_oracle_test.go::TestSessionManagerMatchesPi,test/parity/scenarios/rpc/58-rpc-session-wrong-typed-members.toml - Release note: A session message whose integer member, such as
timestamp, a usage count ordurationMs, is spelled with a fraction or an exponent (Python'sjsonwrites1767225603000.0), holds a fraction, or is beyond the 64-bit range is now kept, as Pi keeps it. Before, PiG dropped the message from the session context and RPCget_messages. The message is written asJSON.stringifywrites the parsed number.
BF-6a6fffa-2
formatToolCallWithArgs against live Pi: new TestFormatToolCallWithArgsMatchesPi…
- Source: PiG's own defect
- Symptom:
formatToolCallWithArgsagainst live Pi: newTestFormatToolCallWithArgsMatchesPi(tui/tool_call_args_oracle_test.go; real dark and light themes; 3,000 seeded argument documents as JSON text with whitespace, repeated keys, escapes, integer-like keys, odd number text, tabs/CR/newlines/emoji, nested values and non-object arguments; collapsed and expanded, colour escapes included). - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/discover_extensions_oracle_test.go::TestDiscoverExtensionPathsMatchPi,internal/codingagent/armin_test.go::TestArminFramesMatchPinnedPi,internal/codingagent/custom_message_components_oracle_test.go::TestCustomMessageAndEntryComponentsMatchPi,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundariesand 9 more in docs/bug-ledger.json - Release note:
formatToolCallWithArgsagainst live Pi: newTestFormatToolCallWithArgsMatchesPi(tui/tool_call_args_oracle_test.go; real dark and light themes; 3,000 seeded argument documents as JSON text with whitespace, repeated keys, escapes, integer-like keys, odd number text, tabs/CR/newlines/emoji, nested values and non-object arguments; collapsed and expanded, colour escapes included). No bug (the earlier port is faithful, including the 100-unit cut and the 97-unit ellipsis); the cut can split a surrogate pair, which PiG keeps as WTF-8 until the terminal write, so the test comparesjsstring.StreamText(got). 11 mutations: 10 red;mutedvstoolOutputis equivalent (both resolve to the same colour in the dark and light themes). Related finding, not changed (no production caller):agent.EstimateTokens/agent.EstimateContextTokens(agent/context_tokens.go) count UTF-8 bytes where Pi's estimateTokens counts UTF-16 units; the production path isinternal/codingagent/compaction(oracle-tested), so theagentcopy is a caller-free duplicate.
BF-6a6fffa-3
getTextOutput against live Pi: new TestGetTextOutputMatchesPi…
- Source: PiG's own defect
- Symptom:
getTextOutputagainst live Pi: newTestGetTextOutputMatchesPi(internal/codingagent/tools/render_text_output_oracle_test.go; 2,500 seeded tool results of text blocks with ANSI, CR, control characters and empty text, and PNG/JPEG/GIF/WebP/garbage/empty image blocks with matching, mismatching, empty and unknown mime types, with images off, kitty or iterm2 and showImages on or off). - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPi,coding/discover_extensions_oracle_test.go::TestDiscoverExtensionPathsMatchPi,internal/codingagent/armin_test.go::TestArminFramesMatchPinnedPi,internal/codingagent/custom_message_components_oracle_test.go::TestCustomMessageAndEntryComponentsMatchPiand 10 more in docs/bug-ledger.json - Release note:
getTextOutputagainst live Pi: newTestGetTextOutputMatchesPi(internal/codingagent/tools/render_text_output_oracle_test.go; 2,500 seeded tool results of text blocks with ANSI, CR, control characters and empty text, and PNG/JPEG/GIF/WebP/garbage/empty image blocks with matching, mismatching, empty and unknown mime types, with images off, kitty or iterm2 and showImages on or off). No bug; 11 mutations: 9 red, 2 equivalent (theData != ""andMimeType != ""guards beforeGetImageDimensions, which returns nothing for empty data or an empty mime type anyway). Not compared: an explicit empty-string mime type (not representable inai.ImageContent).
BF-6a6fffa-4
Live-Pi oracles for utils/headers.ts: TestHeadersToRecordMatchesPi (800 header sequences over case…
- Source: PiG's own defect
- Symptom: Live-Pi oracles for
utils/headers.ts:TestHeadersToRecordMatchesPi(800 header sequences over case variants of 15 names, repeated names, set-cookie, empty values; comparesheadersToRecordwith Pi'sHeaders.entriesrecord) andTestProviderHeadersToRecordMatchesPi(1,500 source lists with null deletes and spelling replacement;providerHeadersToRecord), both inai/headers_utils_oracle_test.go. - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/headers_utils_oracle_test.go::TestHeadersToRecordMatchesPi,ai/headers_utils_oracle_test.go::TestProviderHeadersToRecordMatchesPi,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgainand 2 more in docs/bug-ledger.json - Release note: Live-Pi oracles for
utils/headers.ts:TestHeadersToRecordMatchesPi(800 header sequences over case variants of 15 names, repeated names, set-cookie, empty values; comparesheadersToRecordwith Pi'sHeaders.entriesrecord) andTestProviderHeadersToRecordMatchesPi(1,500 source lists with null deletes and spelling replacement;providerHeadersToRecord), both inai/headers_utils_oracle_test.go. No bug found; 8 of 9 mutations red (last-set-cookie, ", " join, lowercase key, set-cookie special case, delete-before-set, later spelling, undefined result, null handling); the ninth (dropping the empty-value-list skip inheadersToRecord) is equivalent becauseHeadersnever holds a name with no values. Not covered: two spellings of one name inside a singleai.ProviderHeaderssource, which Pi resolves by insertion order and a Go map cannot express (the later key wins in Pi; PiG's result depends on map order).
BF-6a6fffa-5
The Skills page said your own skills live in ~/.pig/skills, that a skill's text is added to the system…
- Source: PiG's own defect
- Symptom: The Skills page said your own skills live in
~/.pig/skills, that a skill's text is added to the system prompt, and that a bare skill name works in a Piglet. - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,test/longsession/session_oracle_test.go::TestSessionManagerMatchesPi,test/parity/scenarios/rpc/58-rpc-session-wrong-typed-members.toml - Release note: The Skills page said your own skills live in
~/.pig/skills, that a skill's text is added to the system prompt, and that a bare skill name works in a Piglet. PiG reads~/.pig/agent/skills, lists each skill's name, description and file in the system prompt (the model reads the text on demand, or you load it with/skill:<name>), andpig piglet validaterejects a skill with no origin. The Skills and Prompt templates pages now carry worked examples with the output PiG produces.
BF-6a6fffa-6
Live-Pi oracle TestOpenAICompletionsCompatAndCacheOptionsPayloadMatchesPi…
- Source: PiG's own defect
- Symptom: Live-Pi oracle
TestOpenAICompletionsCompatAndCacheOptionsPayloadMatchesPi(ai/openai_completions_compat_options_oracle_test.go, 2,500 probes) for the openai-completions compat members and options the other payload oracles never draw: supportsFinishReason, zaiToolStream, supportsOpenAIGrammarTools, supportsMidConvoSystemMessages/ToolAdditions, supportsThinkingTokenBudget, thinkingTokenBudgetField, cacheControlFormat, supportsLongCacheRetention, openRouterRouting, vercelGatewayRouting, cacheRetention and sessionId, over OpenAI/OpenRouter/Vercel/Z.ai/xAI hosts and 0-2 mid-conversation system messages (text, sections, toolsAdded, toolsRemoved). - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/openai_completions_compat_options_oracle_test.go::TestOpenAICompletionsCompatAndCacheOptionsPayloadMatchesPi,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,test/longsession/session_oracle_test.go::TestSessionManagerMatchesPiand 1 more in docs/bug-ledger.json - Release note: Live-Pi oracle
TestOpenAICompletionsCompatAndCacheOptionsPayloadMatchesPi(ai/openai_completions_compat_options_oracle_test.go, 2,500 probes) for the openai-completions compat members and options the other payload oracles never draw: supportsFinishReason, zaiToolStream, supportsOpenAIGrammarTools, supportsMidConvoSystemMessages/ToolAdditions, supportsThinkingTokenBudget, thinkingTokenBudgetField, cacheControlFormat, supportsLongCacheRetention, openRouterRouting, vercelGatewayRouting, cacheRetention and sessionId, over OpenAI/OpenRouter/Vercel/Z.ai/xAI hosts and 0-2 mid-conversation system messages (text, sections, toolsAdded, toolsRemoved). No payload difference found. Mutations: 9 red (zaiToolStream, both routings, long-cache gate, 24h retention, thinking-budget default gate, mid-convo system collapse in both directions, mid-convo tool-addition gate off, prompt-cache gate); equivalent: thetrue &&form of the tool-addition gate (no system message survives collapse without the system flag) and thecompatBooldefault arguments (detected compat always sets each flag). Not reached by this oracle: grammar-tool compat (covered by constrained_sampling_oracle_test.go) and supportsFinishReason (a response-parsing flag). Not covered:sessionIdset to the empty string, which Pi sends asprompt_cache_key: ""andai.StreamOptions.SessionIDcannot tell from unset.
BF-6a6fffa-7
The settings documentation now says that images.blockImages replaces each image sent to the model with…
- Source: PiG's own defect
- Symptom: It said PiG still sent images.
- Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,test/longsession/session_oracle_test.go::TestSessionManagerMatchesPi,test/parity/scenarios/rpc/58-rpc-session-wrong-typed-members.toml - Release note: The settings documentation now says that
images.blockImagesreplaces each image sent to the model withImage reading is disabled., as PiG does. Before, it said PiG still sent images. The embedded onboarding page no longer says that skipping the first-time setup withEscmakes it appear again.
BF-6a6fffa-8
In --mode json, SIGTERM or SIGHUP during a tool call now stops the output and the session file where Pi…
- Source: PiG's own defect
- Symptom: In
--mode json, SIGTERM or SIGHUP during a tool call now stops the output and the session file where Pi does: no tool result, aborted turn oragent_endis written after the signal, and extensions still receivesession_shutdown. - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,test/longsession/session_oracle_test.go::TestSessionManagerMatchesPi,test/parity/scenarios/rpc/58-rpc-session-wrong-typed-members.toml - Release note: In
--mode json, SIGTERM or SIGHUP during a tool call now stops the output and the session file where Pi does: no tool result, aborted turn oragent_endis written after the signal, and extensions still receivesession_shutdown.
BF-6a6fffa-9
TestSettingsSelectorMatchesPi opens each row under the Kitty keyboard protocol with CSI-u keys; no…
- Source: PiG's own defect
- Symptom:
TestSettingsSelectorMatchesPiopens each row under the Kitty keyboard protocol with CSI-u keys; no difference. - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,internal/codingagent/settings_selector_oracle_test.go::TestSettingsSelectorMatchesPi,test/longsession/session_oracle_test.go::TestSessionManagerMatchesPiand 4 more in docs/bug-ledger.json - Release note:
TestSettingsSelectorMatchesPiopens each row under the Kitty keyboard protocol with CSI-u keys; no difference.
BF-6ac4c8c
Ctrl+V pastes clipboard text in Termux
- Source: A fix Pi shipped that PiG ported
- Symptom: Ctrl+V pastes clipboard text in Termux.
- Root cause: Termux reports platform "android" (pi clipboard.ts:55-66 readClipboardText, :133 copyToClipboard, #10391). readClipboardText now runs termux-clipboard-get whenever TERMUX_VERSION is set, outside the linux branch, and the copy error names Termux:API on every platform. Tests: Android read result/empty cases, Android skips Linux commands, Android copy via termux-clipboard-set, the Termux:API error on android and linux, and the Ctrl+V caller path.
- User impact: An operation failed or was rejected
- Platform: Linux, Android and Termux
- Fixed in: 0.5.0
- Fix commits:
6ac4c8c763(2026-10-07) - Tests:
coding/cli/dotenv_autoload_test.go::TestLaunchDirectoryDotenvFilesDoNotReachPigEnvironment,internal/codingagent/clipboard_copy_test.go::TestCopyToClipboardReportsTheTermuxAPIRequirementOnAndroid,internal/codingagent/clipboard_copy_test.go::TestCopyToClipboardTermuxOnAndroidWritesThroughTermuxClipboardSet,internal/codingagent/clipboard_paste_test.go::TestClipboardPasteInsertsTermuxTextOnAndroid,internal/codingagent/clipboard_read_text_test.go::TestReadClipboardTextAndroidSkipsLinuxCommands,internal/codingagent/clipboard_read_text_test.go::TestReadClipboardTextTermuxReadsOnAndroid,internal/codingagent/selfupdate_test.go::TestSelfReplaceAtWithCommitKeepsOnlyTheNewExecutable - Release note: Ctrl+V pastes clipboard text in Termux. PiG read
termux-clipboard-getonly where the platform islinux, and Termux reportsandroid. A failed copy in Termux now also shows the Termux:API install hint on Android.
BF-6cb29f0
Started from a directory reached through a symlink, pig install/remove/list/update resolve local…
- Source: PiG's own defect
- Symptom: Started from a directory reached through a symlink,
pig install/remove/list/updateresolve local sources against the physical working directory, and--sessionwith an empty file records the physical directory as the session cwd, as Pi does. - Root cause: package-manager-cli.ts:951 resolves install/remove/list/update sources against process.cwd(), and SessionManager.open starts an empty session file in process.cwd() (session-manager.ts:1782). Pig took os.Getwd's $PWD, so from a symlinked directory 'pig list' printed a project package's path through the link and '--session empty.jsonl' recorded the link as the session cwd. New binary tests run pig through a symlink with PWD=link. Package tests that set up sources from t.TempDir() and then ran the CLI there now start from the physical temp dir, the directory Pi's process.cwd() would report (they mixed /var and /private/var on macOS).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
630eb9ad7a(2026-10-13),6cb29f0c23(2026-10-13),b2b6d6f226(2026-10-13),b607c35564(2026-10-13) - Tests:
coding/cli/package_cli_chalk_test.go::TestPackageCommandsStyleOutputLikeChalk,coding/cli/package_cli_output_test.go::TestPackageRemovalProgressAndEmptySettings,coding/cli/package_source_parsing_upstream_test.go::TestPackageBareSourcesNeverInvokeNpm,coding/cli/package_ssh_upstream_test.go::TestBareSCPPackageSourceRemainsLocalAtCLIAndIdentityBoundaries,coding/cli/startup_physical_cwd_test.go::TestEmptySessionFileStartsInThePhysicalWorkingDirectory,coding/cli/startup_physical_cwd_test.go::TestPackageListNamesProjectPackagesThroughThePhysicalWorkingDirectory,coding/cli/startup_physical_cwd_test.go::TestStartupWorkingDirectoryIsThePhysicalPath,internal/codingagent/bug_report_test.go::TestBugExportWritesArchiveRecordsSessionAndLinksPigIssueand 3 more in docs/bug-ledger.json - Release note: Started from a directory reached through a symlink,
pig install/remove/list/updateresolve local sources against the physical working directory, and--sessionwith an empty file records the physical directory as the session cwd, as Pi does.
BF-6cb29f0-2
Started from a directory reached through a symlink, /export <file>.jsonl and /bug now write and name…
- Source: PiG's own defect
- Symptom: Started from a directory reached through a symlink,
/export <file>.jsonland/bugnow write and name their files under the physical working directory, as Pi does. - Root cause: package-manager-cli.ts:951 resolves install/remove/list/update sources against process.cwd(), and SessionManager.open starts an empty session file in process.cwd() (session-manager.ts:1782). Pig took os.Getwd's $PWD, so from a symlinked directory 'pig list' printed a project package's path through the link and '--session empty.jsonl' recorded the link as the session cwd. New binary tests run pig through a symlink with PWD=link. Package tests that set up sources from t.TempDir() and then ran the CLI there now start from the physical temp dir, the directory Pi's process.cwd() would report (they mixed /var and /private/var on macOS).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
630eb9ad7a(2026-10-13),68b129d018(2026-10-13),6cb29f0c23(2026-10-13),b2b6d6f226(2026-10-13),b607c35564(2026-10-13) - Tests:
coding/cli/package_cli_chalk_test.go::TestPackageCommandsStyleOutputLikeChalk,coding/cli/package_cli_output_test.go::TestPackageRemovalProgressAndEmptySettings,coding/cli/package_source_parsing_upstream_test.go::TestPackageBareSourcesNeverInvokeNpm,coding/cli/package_ssh_upstream_test.go::TestBareSCPPackageSourceRemainsLocalAtCLIAndIdentityBoundaries,coding/cli/startup_physical_cwd_test.go::TestEmptySessionFileStartsInThePhysicalWorkingDirectory,coding/cli/startup_physical_cwd_test.go::TestPackageListNamesProjectPackagesThroughThePhysicalWorkingDirectory,coding/cli/startup_physical_cwd_test.go::TestStartupWorkingDirectoryIsThePhysicalPath,internal/codingagent/bug_report_test.go::TestBugExportWritesArchiveRecordsSessionAndLinksPigIssueand 4 more in docs/bug-ledger.json - Release note: Started from a directory reached through a symlink,
/export <file>.jsonland/bugnow write and name their files under the physical working directory, as Pi does.
BF-6cb29f0-3
Switching to or importing an empty session file from a directory reached through a symlink starts the session…
- Source: PiG's own defect
- Symptom: Switching to or importing an empty session file from a directory reached through a symlink starts the session in the physical working directory, as Pi does.
- Root cause: package-manager-cli.ts:951 resolves install/remove/list/update sources against process.cwd(), and SessionManager.open starts an empty session file in process.cwd() (session-manager.ts:1782). Pig took os.Getwd's $PWD, so from a symlinked directory 'pig list' printed a project package's path through the link and '--session empty.jsonl' recorded the link as the session cwd. New binary tests run pig through a symlink with PWD=link. Package tests that set up sources from t.TempDir() and then ran the CLI there now start from the physical temp dir, the directory Pi's process.cwd() would report (they mixed /var and /private/var on macOS).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6cb29f0c23(2026-10-13),b2b6d6f226(2026-10-13),b607c35564(2026-10-13) - Tests:
coding/cli/package_cli_chalk_test.go::TestPackageCommandsStyleOutputLikeChalk,coding/cli/package_cli_output_test.go::TestPackageRemovalProgressAndEmptySettings,coding/cli/package_source_parsing_upstream_test.go::TestPackageBareSourcesNeverInvokeNpm,coding/cli/package_ssh_upstream_test.go::TestBareSCPPackageSourceRemainsLocalAtCLIAndIdentityBoundaries,coding/cli/startup_physical_cwd_test.go::TestEmptySessionFileStartsInThePhysicalWorkingDirectory,coding/cli/startup_physical_cwd_test.go::TestPackageListNamesProjectPackagesThroughThePhysicalWorkingDirectory,coding/cli/startup_physical_cwd_test.go::TestStartupWorkingDirectoryIsThePhysicalPath,internal/codingagent/bug_report_test.go::TestBugExportWritesArchiveRecordsSessionAndLinksPigIssue - Release note: Switching to or importing an empty session file from a directory reached through a symlink starts the session in the physical working directory, as Pi does.
BF-6ce1db1
In print and JSON mode, SIGTERM and SIGHUP always leave their line in exit.log
- Source: PiG's own defect
- Symptom: In print and JSON mode, SIGTERM and SIGHUP always leave their line in
exit.log. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
6ce1db1b0d(2026-10-11) - Tests: None recorded.
- Release note: In print and JSON mode, SIGTERM and SIGHUP always leave their line in
exit.log.
BF-6d0ec02
Durable (Pi eb2fce43): a wait that closes a cycle through waits and held owners now fails the task's commit…
- Source: PiG's own defect
- Symptom: Every task on such a cycle waited forever.
- Root cause: A background task may now wait on the owner of its conversation, because that wait is no cycle, and a task may wait on an ended owner.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),6d0ec024f2(2026-10-12) - Tests:
durable/core/driver/remote_error_names_test.go::TestRemoteErrorMapsToATypedErrorOnlyByItsClassName,durable/errors_session_failed_test.go::TestStorageRequestErrorAndSessionFailedMatchErrorsTS,durable/harness/harness_structured_test.go::TestWaiting,durable/harness/scheduler_retention_test.go::TestSchedulerRetention,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhaseand 3 more in docs/bug-ledger.json - Release note: Durable (Pi eb2fce43): a wait that closes a cycle through waits and held owners now fails the task's commit with
Task <id> cannot wait on <member>: <cycle> is a wait cycle, which names the cycle. Before, every task on such a cycle waited forever. The only cycles caught before were a task waiting on itself or on a task in its owner chain. A background task may now wait on the owner of its conversation, because that wait is no cycle, and a task may wait on an ended owner.
BF-6d3cfc8
A user_bash operations object from an extension is no longer released while a command is running through it
- Source: PiG's own defect
- Symptom: A user_bash
operationsobject from an extension is no longer released while a command is running through it. - Root cause: The extension's handle is released when the host drops the object, but the running exec did not keep the object reachable, so a garbage collection mid-command could delete the extension's table entry under the command.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
6d3cfc863d(2026-10-21, #278) - Tests:
coding/extension/host/subprocess/user_bash_operations_test.go::TestUserBashOperationsAreNotReleasedWhileAnExecRuns - Release note: A user_bash
operationsobject from an extension is no longer released while a command is running through it. The extension's handle is released when the host drops the object, but the running exec did not keep the object reachable, so a garbage collection mid-command could delete the extension's table entry under the command.
BF-6d9b403
The /config selector keeps the first-seen order of groups that tie on origin, scope and source (auto…
- Source: PiG's own defect
- Symptom: The /config selector keeps the first-seen order of groups that tie on origin, scope and source (auto resources from different base directories); it followed Go map order (
tui/config_selector.goBuildResourceGroups, config-selector.ts buildGroups; regressionTestBuildResourceGroupsTiesKeepFirstSeenOrder). - Root cause: Pi's buildGroups uses a Map (insertion order) and a stable sort; Pig collected groups from a Go map, so groups tying on origin, scope and source (auto resources from different baseDirs) came out in random order.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6d9b403c0a(2026-10-11) - Tests:
tui/config_selector_test.go::TestBuildResourceGroupsTiesKeepFirstSeenOrder - Release note: The /config selector keeps the first-seen order of groups that tie on origin, scope and source (auto resources from different base directories); it followed Go map order (
tui/config_selector.goBuildResourceGroups, config-selector.ts buildGroups; regressionTestBuildResourceGroupsTiesKeepFirstSeenOrder).
BF-6debb12
fix(train): gate repairs after merging train 3 and the ordering stack
- Source: PiG's own defect
- Symptom: fix(train): gate repairs after merging train 3 and the ordering stack
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
6debb12f2e(2026-10-21) - Tests: None recorded.
BF-6dfa6bf
go install github.com/MichaelKinsy/PiG/cmd/pig@<version> downloads a root module of 181 MB instead of 304 MB
- Source: PiG's own defect
- Symptom:
go install github.com/MichaelKinsy/PiG/cmd/pig@<version>downloads a root module of 181 MB instead of 304 MB. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
6dfa6bfa3b(2026-10-19, #226) - Tests:
automation/release/modulesize/main_test.go::TestCommittedBudgetStaysFarBelowTheGoLimit,automation/release/modulesize/main_test.go::TestMeasureCountsOnlyWhatGoPutsInTheModuleZip,automation/release/modulesize/main_test.go::TestOverBudgetListsTheLargestFilesAndDirectories,automation/release/modulesize/main_test.go::TestReadBudget - Release note:
go install github.com/MichaelKinsy/PiG/cmd/pig@<version>downloads a root module of 181 MB instead of 304 MB. Nine data-only directories (parity interface ledgers, pin ledgers, upstream-sync ledgers and large test data) have their owngo.mod, so Go leaves them out of the root module zip. The files stay in the repository.
BF-6e6a0dd
A session message with a member of another JSON type than Pi writes is now kept, as Pi keeps it
- Source: PiG's own defect
- Symptom: PiG dropped the message from the session context, the projection and RPC
get_messages. - Root cause: parseSessionEntryLine keeps any JSON.parse value as an entry's message (session-manager.ts:617-623), and buildSessionContext and the projection push it as is, so Pi keeps a message that is a number, a string or an array in its context, projection and get_messages. PiG dropped it. decodeLax now decodes such a message as a custom message without members that keeps its text, and SourceJSON writes that text. Having no role, it reaches no provider, as Pi's convertToLlm default case returns undefined (messages.ts:189-192). A null message stays undecodable: Pi's context build throws reading its role (getSessionContextSettings).
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6e6a0dd1cc(2026-10-10) - Tests:
agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,internal/codingagent/session_undecodable_test.go::TestUndecodableEntriesAreCountedAndOmitted,internal/sessionentry/union_test.go::TestDecodeSessionEntryKeepsRecordsNoMemberDescribes,test/longsession/session_oracle_test.go::TestSessionManagerMatchesPi - Release note: A session message with a member of another JSON type than Pi writes is now kept, as Pi keeps it. Examples are a string
timestamp, numericcontent, a text block whosetextis a number, a usage count written as a string,isError: "no", a numeric or missingrole, and a message that is a number, a string or an array rather than an object. Before, PiG dropped the message from the session context, the projection and RPCget_messages. The message is written exactly as it was read. PiG reads a wrong-typed member as absent. An object or array member keeps the parts that have the right types. A content block with an unknown or wrong-typedtypeis left out of the provider request, as Pi's provider conversions skip it.
BF-6ee6eae
A Session delivers each event to its listeners the way Pi's AgentSession does
- Source: PiG's own defect
- Symptom: Every delivery used the list as it was when the delivery began.
- Root cause: EmitMessage/EmitError/EmitClose now visit the next entry registered after the last one run. New tests: the live-set emit, the closed-end drop and placement hook of InMemoryTransport, the redirect mix-up page and context cause of OAuthCallbackServer, SetProtocolVersion's header, McpAbortError's defaults. 71 mutants recorded; equivalents listed in evidence.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1278df8e33(2026-10-10),6ee6eae918(2026-10-10) - Tests:
coding/codemode_session_upstream_test.go::TestUpstreamCodemodeModels,coding/session_boundaries_drafts_upstream_test.go::TestUpstreamSessionBoundariesDrafts,coding/session_event_listener_upstream_test.go::TestSessionNotifyListenersWalksTheLiveListenerListAsPi,coding/session_event_listener_upstream_test.go::TestSessionSubscribeNotifiesEveryListenerInOrderAndUnsubscribeLeavesTheOthers,coding/session_sections_test.go::TestSessionDefaultSectionsReachEventsProviderAndPersistenceOnce,mcp/errors_abort_test.go::TestMcpAbortErrorHasPiDefaultsNameAndNoCause,mcp/mcptest/inmemory_test.go::TestInMemoryTransportDropsQueuedMessagesOfAClosedEndAndReportsPlacement,mcp/oauth/callback_server_options_test.go::TestOAuthCallbackServerNamesTheRedirectMixUpAndReturnsTheContextCauseand 2 more in docs/bug-ledger.json - Release note: A Session delivers each event to its listeners the way Pi's
AgentSessiondoes. A listener that unsubscribes itself or another listener during delivery changes which listeners that delivery still reaches, and a listener subscribed during delivery receives the same event. Before, every delivery used the list as it was when the delivery began.
BF-6f26a84
Xiaomi MiMo models send only the thinking on/off toggle (no reasoning_effort) and expose only the off and…
- Source: PiG's own defect
- Symptom: Xiaomi MiMo models send only the thinking on/off toggle (no
reasoning_effort) and expose only theoffandhighthinking levels, fixing the 400 atminimal. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6f26a84f7f(2026-10-14, #10818) - Tests:
ai/openai_tool_choice_formats_upstream_test.go::TestOpenAICompletionsToolChoiceReplayAndFormatsUpstream,ai/xiaomi_models_upstream_test.go::TestXiaomiReasoningModelsAreAnOffOnToggleUpstream - Release note: Xiaomi MiMo models send only the thinking on/off toggle (no
reasoning_effort) and expose only theoffandhighthinking levels, fixing the 400 atminimal. Carried forward from Pi 6374c2ba and 381a2d8e (earendil-works/pi#10818), past the 1.2.0+3daa2028 pin.
BF-6f4a82f
Validation errors for propertyNames name the failing member in their path, contains reports only "must…
- Source: PiG's own defect
- Symptom: Validation errors for
propertyNamesname the failing member in their path,containsreports only "must contain at least 1 valid item", and a failingthenbranch reports onlymust match "then" schemawhile a failingelsebranch also lists its errors, as in Pi. - Root cause: validateToolArguments (packages/ai/src/utils/validation.ts, with typebox 1.3.27 Compile/Value.Convert/Check/Errors from internal/experimental/protocol/testdata/typebox, testdata/validate_tool_args.mjs) runs on 12000 seeded schemas (scalars, type unions, enum, const, bounds, patterns, objects with required/additionalProperties/patternProperties/dependentRequired, arrays, anyOf/oneOf/allOf, defaults) and argument values (right types, stringified numbers and booleans, nulls, wrong shapes, big numbers). The coerced result or the whole error message must match. 118 of 6000 differed before (red): (1) TypeBox's error context keeps the first 8 errors (Settings.maxErrors) and Go listed all; (2)…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),e8cc072521(2026-10-08),6f4a82fd86(2026-10-09),97e178f7f6(2026-10-09),d490a8b161(2026-10-09) - Tests:
ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,ai/validate_tool_args_oracle_test.go::TestValidateToolArgumentsMatchesPiOnSeededSchemas,chord/delta/own_key_order_test.go::TestDiffRevisionsVisitsArrayIndexKeysFirstInNumericOrder,internal/chord/chordjson/own_key_order_test.go::TestCopyFollowsPisKeyOrderAndMessages,internal/evals/plan_oracle_test.go::TestPlanMatchesPiOnSeededProbes,internal/evals/report_oracle_test.go::TestEvalReportMatchesPiOnSeededDesigns - Release note: Validation errors for
propertyNamesname the failing member in their path,containsreports only "must contain at least 1 valid item", and a failingthenbranch reports onlymust match "then" schemawhile a failingelsebranch also lists its errors, as in Pi.
BF-6f7daf1
fix: clone Windows Git Packages with core.longpaths so a deep file:// source path fits
- Source: PiG's own defect
- Symptom: fix: clone Windows Git Packages with core.longpaths so a deep file:// source path fits
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: Unreleased
- Fix commits:
6f7daf115e(2026-10-22) - Tests: None recorded.
BF-6fb1307
openai-codex-responses reads instructions from the initial system message of the resolved transcript before…
- Source: PiG's own defect
- Symptom: openai-codex-responses reads
instructionsfrom the initial system message of the resolved transcript beforetransformMessagesdrops an errored or aborted assistant turn (ai/openai_responses.go, openai-codex-responses.ts:551): a transcript that starts with such a turn followed by a system message no longer sends that message as the prompt. - Root cause: Real fixes: anthropic.go buildParams initial system message from the untransformed transcript; managed effort 'high' unless forceAdaptiveThinking; unmapped xhigh/max -> high; managed simple max_tokens adjustment. openai_responses.go: codex instruction role from the selected model's reasoning flag.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),6fb1307525(2026-10-11),81e584bdba(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/anthropic_effort_strict_test.go::TestAnthropicManagedEffortMapping,ai/anthropic_messages_compat_options_oracle_test.go::TestAnthropicMessagesCompatAndOptionsPayloadMatchesPi,ai/bedrock_payload_oracle_test.go::TestBedrockConversePayloadMatchesPi,ai/mistral_messages_payload_oracle_test.go::TestMistralMessagesPayloadMatchesPiand 5 more in docs/bug-ledger.json - Release note: openai-codex-responses reads
instructionsfrom the initial system message of the resolved transcript beforetransformMessagesdrops an errored or aborted assistant turn (ai/openai_responses.go, openai-codex-responses.ts:551): a transcript that starts with such a turn followed by a system message no longer sends that message as the prompt. Found by drawing the responses compat members the payload oracle never drew (supportsDeveloperRole,supportsAdditionalTools,supportsToolSearch,supportsExplicitPromptCacheMode,sessionAffinityFormat,supportsOpenAIGrammarTools) incheckResponsesPayload; 5 mutants of those flags and the codex initial-system source go red (grammar tools stay inert there: the drawn tools carry noconstrainedSampling, covered byconstrained_sampling_oracle_test.go).
BF-70d51e5
Fixed bash and PowerShell results losing Took after reloading a session, and the live Took including…
- Source: A fix Pi shipped that PiG ported
- Symptom: Fixed bash and PowerShell results losing
Tookafter reloading a session, and the liveTookincluding wall-clock steps; both now show the recorded execution time. - Root cause: durationMs (agent-loop.ts executePreparedToolCall, createToolResultMessage, emitToolExecutionEnd; extensions/types.ts ToolRenderContext and ToolExecutionEndEvent; nested-tool-calls.ts; renderers/bash.ts): - agent loop records execute() time in whole ms on a monotonic clock, excluding hooks, on the tool result message (between isError and timestamp), tool_execution_end (JSON/RPC and extensions, all SDKs) and RunToolCall outcomes; a call that did not run has none; a throwing tool still has one. - the tool render context carries durationMs (nil while partial, for a call that did not run and for legacy results); bash/PowerShell Took shows the recorded value live and after a session reload…
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
70d51e54f4(2026-10-07) - Tests:
agent/agent_loop_upstream_test.go::TestAgentLoop_RecordsDurationOfThrowingTool,agent/agent_loop_upstream_test.go::TestAgentLoop_RecordsToolExecutionDurationExcludingHooks,agent/agent_loop_upstream_test.go::TestRunToolCallOutcomeCarriesDurationMs,agent/message_json_test.go::TestAgentMessageJSONToolResultDurationMs,agent/tool_result_fields_test.go::TestToolResultMessageFieldsMatchUpstream,coding/nested_tool_call_events_test.go::TestNestedToolCallEndEventForwardsOutcomeDurationMs,coding/session_tool_duration_test.go::TestSessionRecordsToolDurationOnEventsAndPersistedResult,internal/codingagent/interactive_resume_tools_test.go::TestInteractiveMode_ResumeShellToolsShowRecordedDurationand 8 more in docs/bug-ledger.json - Release note: Fixed bash and PowerShell results losing
Tookafter reloading a session, and the liveTookincluding wall-clock steps; both now show the recorded execution time.
BF-70d51e5-2
Fixed the message-format page: it said PiG does not carry the nestedCalls record on tool result messages…
- Source: A fix Pi shipped that PiG ported
- Symptom: Fixed the message-format page: it said PiG does not carry the
nestedCallsrecord on tool result messages, which it has written since 1.0.4, and it now lists the field; the page also stops citing areplacefield that Pi's page no longer lists. - Root cause: durationMs (agent-loop.ts executePreparedToolCall, createToolResultMessage, emitToolExecutionEnd; extensions/types.ts ToolRenderContext and ToolExecutionEndEvent; nested-tool-calls.ts; renderers/bash.ts): - agent loop records execute() time in whole ms on a monotonic clock, excluding hooks, on the tool result message (between isError and timestamp), tool_execution_end (JSON/RPC and extensions, all SDKs) and RunToolCall outcomes; a call that did not run has none; a throwing tool still has one. - the tool render context carries durationMs (nil while partial, for a call that did not run and for legacy results); bash/PowerShell Took shows the recorded value live and after a session reload…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
70d51e54f4(2026-10-07) - Tests:
agent/agent_loop_upstream_test.go::TestAgentLoop_RecordsDurationOfThrowingTool,agent/agent_loop_upstream_test.go::TestAgentLoop_RecordsToolExecutionDurationExcludingHooks,agent/agent_loop_upstream_test.go::TestRunToolCallOutcomeCarriesDurationMs,agent/message_json_test.go::TestAgentMessageJSONToolResultDurationMs,agent/tool_result_fields_test.go::TestToolResultMessageFieldsMatchUpstream,coding/nested_tool_call_events_test.go::TestNestedToolCallEndEventForwardsOutcomeDurationMs,coding/session_tool_duration_test.go::TestSessionRecordsToolDurationOnEventsAndPersistedResult,internal/codingagent/interactive_resume_tools_test.go::TestInteractiveMode_ResumeShellToolsShowRecordedDurationand 8 more in docs/bug-ledger.json - Release note: Fixed the message-format page: it said PiG does not carry the
nestedCallsrecord on tool result messages, which it has written since 1.0.4, and it now lists the field; the page also stops citing areplacefield that Pi's page no longer lists.
BF-70d8a41
Keybinding conflict detection ignores user bindings for actions the table does not define, as Pi's…
- Source: PiG's own defect
- Symptom: Keybinding conflict detection ignores user bindings for actions the table does not define, as Pi's
KeybindingsManager.rebuilddoes, so a typo'd action no longer appears in a conflict (oraclesTestKeybindingsManagerMatchesPi,TestAppKeybindingsManagerMatchesPi). - Root cause: Matching already agreed everywhere. Fixed in both managers; reverting either fails the matching test (2 failures). Pi orders conflicts by the user's JSON key order, which Pig's map decoding does not keep, so conflicts compare as sorted sets.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
70d8a41e14(2026-10-07) - Tests:
internal/codingagent/app_keybindings_oracle_test.go::TestAppKeybindingsManagerMatchesPi,tui/keybindings_matches_oracle_test.go::TestKeybindingsManagerMatchesPi - Release note: Keybinding conflict detection ignores user bindings for actions the table does not define, as Pi's
KeybindingsManager.rebuilddoes, so a typo'd action no longer appears in a conflict (oraclesTestKeybindingsManagerMatchesPi,TestAppKeybindingsManagerMatchesPi).
BF-70e1841
/settings warnings submenu: a warning the settings never set shows as on and a toggle always reports the…
- Source: PiG's own defect
- Symptom: /settings warnings submenu: a warning the settings never set shows as on and a toggle always reports the warning as set (Pi's
?? true). - Root cause: Every callback with its arguments, the callback count after each key, and every frame's text are compared; Pi's callbacks reached include onThemePreview, onThemeChange, onModelThinkingLevelChange/Remove and 30 more. Pi has no PiG 'Mask secret input' row (D80), so the driver inserts that row and its callback into a temp copy of Pi's settings-selector.js at Pig's position. One real difference: Pi's WarningSettingsSubmenu shows '(this.state.anthropicExtraUsage ?? true)' and always writes the toggled value; Pig's submenu used the Go zero value, so a warning the settings never set showed as off and its first toggle reported {} (the Set flag was never set). It now defaults to on and marks the…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d1f258f335(2026-10-06),70e1841cd8(2026-10-07),c8e3d86378(2026-10-07) - Tests:
internal/codingagent/settings_selector_oracle_test.go::TestSettingsSelectorMatchesPi,internal/codingagent/settings_submenu_oracle_test.go::TestSettingsSubmenusMatchPi,test/parity/scenarios/settings/08-settings-search-editing.toml,tui/settings_list_input_test.go::TestSettingsListAnyDelegatedKeyResetsTheSelectionToTheFirstMatch,tui/settings_list_input_test.go::TestSettingsListBoundActionsRunInUpstreamOrder,tui/settings_list_input_test.go::TestSettingsListEmptyAndSingletonNavigationBoundaries,tui/settings_list_input_test.go::TestSettingsListSearchDelegatesEditingKeysToTheInput,tui/settings_list_input_test.go::TestSettingsListSubmenuReceivesEveryKeyIncludingCanceland 2 more in docs/bug-ledger.json - Release note: /settings warnings submenu: a warning the settings never set shows as on and a toggle always reports the warning as set (Pi's
?? true).
BF-7177d5f
fix(tui): card of an unregistered tool is Pi's formatToolExecution
- Source: PiG's own defect
- Symptom: fix(tui): card of an unregistered tool is Pi's formatToolExecution
- Root cause: tool-execution.ts updateDisplay draws a tool without a definition (hasRendererDefinition false, e.g. a hallucinated tool name) as one Text: toolTitle name, a blank line, JSON.stringify(args, null, 2) and the ANSI-stripped result on the next line. Pig drew a compact 'x:1' summary (with sorted keys, cut at 40/80 chars) and a blank row before the result. Found by running scripted replies through the real Pi and Pig (mock OpenAI-completions server, tmux, session files compared too). TestUnregisteredToolCardIsUpstreamFormatToolExecution is red without the change; the two component-only tests that used a definition-less card for a registered tool now give it a definition.…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
7177d5f92b(2026-10-11) - Tests:
tui/summary_output_pad_test.go::TestToolAndBashBlocksFollowOutputPad,tui/tool_execution_test.go::TestToolExecutionStreamingFallbackUsesLeadingPreview,tui/tool_execution_test.go::TestUnregisteredToolCardIsUpstreamFormatToolExecution
BF-72e7ab2
A tool card whose app.tools.expand key is unbound shows an empty key in "..
- Source: PiG's own defect
- Symptom: A tool card whose
app.tools.expandkey is unbound shows an empty key in "... - Root cause: Pi's keyText answers "" for an action with no key bound; AppKeyText returned the fallback default whenever the installed resolver answered empty, so the tool card expand hint kept 'ctrl+o' after the user unbound app.tools.expand. The fallback now applies only when no resolver is installed, as its doc says. Reverting the fix fails them.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
72e7ab2dbf(2026-10-11),c4ad739eb8(2026-10-11),fac470319e(2026-10-11) - Tests:
coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,internal/codingagent/builtin_tool_cards_oracle_test.go::TestBuiltinToolCardsMatchPi,internal/codingagent/extension_editor_test.go::TestExtensionEditorHandleInputMatchesPi,tui/extension_input_oracle_test.go::TestExtensionInputMatchesPi,tui/extension_selector_oracle_test.go::TestExtensionSelectorMatchesPi - Release note: A tool card whose
app.tools.expandkey is unbound shows an empty key in "... (N more lines, to expand)" as Pi does, instead of the defaultctrl+o(tui/keybinding_hints.goAppKeyText falls back only when no resolver is installed;TestBuiltinToolCardsMatchPigained rebound/multi-key/unbound expand probes, 5 red before).
BF-72e7ab2-2
TestMcpManagerViewMatchesPi renders the /mcp manager footers under rebound, multi-key and unbound…
- Source: PiG's own defect
- Symptom:
TestMcpManagerViewMatchesPirenders the /mcp manager footers under rebound, multi-key and unboundtui.select.confirm/cancel/up/down (a quarter of its 120 probes, against the merged tui and app table); no difference; the hard-coded-hint mutation is red (a matching-path mutation is not observable in the rendered rows). - Root cause: Pi's keyText answers "" for an action with no key bound; AppKeyText returned the fallback default whenever the installed resolver answered empty, so the tool card expand hint kept 'ctrl+o' after the user unbound app.tools.expand. The fallback now applies only when no resolver is installed, as its doc says. Reverting the fix fails them.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),33fe10d58e(2026-10-11),72e7ab2dbf(2026-10-11),c4ad739eb8(2026-10-11),fac470319e(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/models_error_oracle_test.go::TestModelsErrorMessageMatchesPi,coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,coding/mcpext/manager_view_oracle_test.go::TestMcpManagerViewMatchesPiand 8 more in docs/bug-ledger.json - Release note:
TestMcpManagerViewMatchesPirenders the /mcp manager footers under rebound, multi-key and unboundtui.select.confirm/cancel/up/down (a quarter of its 120 probes, against the merged tui and app table); no difference; the hard-coded-hint mutation is red (a matching-path mutation is not observable in the rendered rows).
BF-72e7ab2-3
TestLlamaViewMatchesPi runs a quarter of its 700 scenarios under rebound, multi-key and unbound…
- Source: PiG's own defect
- Symptom:
TestLlamaViewMatchesPiruns a quarter of its 700 scenarios under rebound, multi-key and unboundtui.select.*bindings (hint rows and the progress view's cancel key); no difference; mutations on the hint key, the stop hint and the progress cancel match are red. - Root cause: Pi's keyText answers "" for an action with no key bound; AppKeyText returned the fallback default whenever the installed resolver answered empty, so the tool card expand hint kept 'ctrl+o' after the user unbound app.tools.expand. The fallback now applies only when no resolver is installed, as its doc says. Reverting the fix fails them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),33fe10d58e(2026-10-11),72e7ab2dbf(2026-10-11),c4ad739eb8(2026-10-11),fac470319e(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/models_error_oracle_test.go::TestModelsErrorMessageMatchesPi,coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,coding/mcpext/manager_view_oracle_test.go::TestMcpManagerViewMatchesPiand 9 more in docs/bug-ledger.json - Release note:
TestLlamaViewMatchesPiruns a quarter of its 700 scenarios under rebound, multi-key and unboundtui.select.*bindings (hint rows and the progress view's cancel key); no difference; mutations on the hint key, the stop hint and the progress cancel match are red.
BF-72e7ab2-4
TestCodemodeCardsMatchPi and TestMcpToolCardsMatchPi repeat their collapsed probes under rebound…
- Source: PiG's own defect
- Symptom:
TestCodemodeCardsMatchPiandTestMcpToolCardsMatchPirepeat their collapsed probes under rebound, multi-key and unboundapp.tools.expand(the expand hint of the codemode and MCP cards); no difference; hard-coded-key mutations are red. - Root cause: Pi's keyText answers "" for an action with no key bound; AppKeyText returned the fallback default whenever the installed resolver answered empty, so the tool card expand hint kept 'ctrl+o' after the user unbound app.tools.expand. The fallback now applies only when no resolver is installed, as its doc says. Reverting the fix fails them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
33fe10d58e(2026-10-11),72e7ab2dbf(2026-10-11),c4ad739eb8(2026-10-11),fac470319e(2026-10-11) - Tests:
ai/models_error_oracle_test.go::TestModelsErrorMessageMatchesPi,coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,coding/mcpext/manager_view_oracle_test.go::TestMcpManagerViewMatchesPi,internal/codingagent/builtin_tool_cards_oracle_test.go::TestBuiltinToolCardsMatchPi,internal/codingagent/extension_editor_test.go::TestExtensionEditorHandleInputMatchesPi,internal/codingagent/llama/ui_oracle_test.go::TestLlamaViewMatchesPi,internal/codingagent/mcp_cards_oracle_test.go::TestMcpToolCardsMatchPiand 2 more in docs/bug-ledger.json - Release note:
TestCodemodeCardsMatchPiandTestMcpToolCardsMatchPirepeat their collapsed probes under rebound, multi-key and unboundapp.tools.expand(the expand hint of the codemode and MCP cards); no difference; hard-coded-key mutations are red.
BF-72e7ab2-5
TestBashExecutionComponentMatchesPi probes the collapsed expand hint under rebound, multi-key and unbound…
- Source: PiG's own defect
- Symptom:
TestBashExecutionComponentMatchesPiprobes the collapsed expand hint under rebound, multi-key and unboundapp.tools.expandwith the merged tui and app table; no difference; the fixed-key mutation is red. - Root cause: Pi's keyText answers "" for an action with no key bound; AppKeyText returned the fallback default whenever the installed resolver answered empty, so the tool card expand hint kept 'ctrl+o' after the user unbound app.tools.expand. The fallback now applies only when no resolver is installed, as its doc says. Reverting the fix fails them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
33fe10d58e(2026-10-11),72e7ab2dbf(2026-10-11),c4ad739eb8(2026-10-11),fac470319e(2026-10-11) - Tests:
ai/models_error_oracle_test.go::TestModelsErrorMessageMatchesPi,coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,coding/mcpext/manager_view_oracle_test.go::TestMcpManagerViewMatchesPi,internal/codingagent/bash_execution_oracle_test.go::TestBashExecutionComponentMatchesPi,internal/codingagent/builtin_tool_cards_oracle_test.go::TestBuiltinToolCardsMatchPi,internal/codingagent/extension_editor_test.go::TestExtensionEditorHandleInputMatchesPi,internal/codingagent/llama/ui_oracle_test.go::TestLlamaViewMatchesPiand 3 more in docs/bug-ledger.json - Release note:
TestBashExecutionComponentMatchesPiprobes the collapsed expand hint under rebound, multi-key and unboundapp.tools.expandwith the merged tui and app table; no difference; the fixed-key mutation is red.
BF-72e7ab2-6
TestSummaryAndSkillMessageComponentsMatchPi probes the compaction, branch summary and skill messages under…
- Source: PiG's own defect
- Symptom:
TestSummaryAndSkillMessageComponentsMatchPiprobes the compaction, branch summary and skill messages under rebound, multi-key and unboundapp.tools.expand; no difference; fixed-key mutations in each component are red. - Root cause: Pi's keyText answers "" for an action with no key bound; AppKeyText returned the fallback default whenever the installed resolver answered empty, so the tool card expand hint kept 'ctrl+o' after the user unbound app.tools.expand. The fallback now applies only when no resolver is installed, as its doc says. Reverting the fix fails them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),33fe10d58e(2026-10-11),72e7ab2dbf(2026-10-11),c4ad739eb8(2026-10-11),fac470319e(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/models_error_oracle_test.go::TestModelsErrorMessageMatchesPi,coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,coding/mcpext/manager_view_oracle_test.go::TestMcpManagerViewMatchesPiand 12 more in docs/bug-ledger.json - Release note:
TestSummaryAndSkillMessageComponentsMatchPiprobes the compaction, branch summary and skill messages under rebound, multi-key and unboundapp.tools.expand; no difference; fixed-key mutations in each component are red.
BF-731474b
coding.DefineTool returns the tool definition it is given, as Pi's defineTool does
- Source: PiG's own defect
- Symptom:
coding.DefineToolreturns the tool definition it is given, as Pi'sdefineTooldoes. - Root cause: Designed-out rows: 247 to 215.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
731474b0eb(2026-10-10) - Tests:
coding/define_tool_test.go::TestDefineToolReturnsTheDefinitionUnchanged - Release note:
coding.DefineToolreturns the tool definition it is given, as Pi'sdefineTooldoes.
BF-731474b-2
codemode.CodemodeJsonSchema names the raw JSON of a tool's JSON Schema document, as Pi's…
- Source: PiG's own defect
- Symptom:
codemode.CodemodeJsonSchemanames the raw JSON of a tool's JSON Schema document, as Pi'sCodemodeJsonSchemadoes. - Root cause: Designed-out rows: 247 to 215.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
731474b0eb(2026-10-10) - Tests:
coding/define_tool_test.go::TestDefineToolReturnsTheDefinitionUnchanged - Release note:
codemode.CodemodeJsonSchemanames the raw JSON of a tool's JSON Schema document, as Pi'sCodemodeJsonSchemadoes.
BF-7375fe4
After pig piglet update installs a new release of a pulled Piglet Binary, pig piglet list shows the…
- Source: PiG's own defect
- Symptom: Each release of a Piglet without a source was listed as its own Piglet and
showprinted only the first. - Root cause: After
pig piglet updateinstalled a new release of a pulled Piglet Binary, each installed release became its own binary-only Piglet:pig piglet listprinted the name twice andpig piglet show <name>printed only the first release, not the one the update installed. Records without a matching source now join the one binary-only Piglet of their name, and the list names a target that several releases share once. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
265b6a9022(2026-10-07),7375fe4edc(2026-10-07) - Tests:
coding/piglet/prune_test.go::TestPigletPruneRemovesOldBuildsOnly,coding/piglet/resolve_test.go::TestListGroupsBinaryOnlyRecordsOfOnePiglet,coding/piglet/signature/verifycache_test.go::TestCheckCached_ChangedIdentityOrPolicyRehashes,coding/piglet/signature/verifycache_test.go::TestCheckCached_EntryForAnotherPathIsNotTrusted,coding/piglet/signature/verifycache_test.go::TestCheckCached_FailedVerificationIsNeverRemembered,coding/piglet/signature/verifycache_test.go::TestCheckCached_FileChangedDuringTheHashIsNotRemembered,coding/piglet/signature/verifycache_test.go::TestCheckCached_KeepsOneEntryPerPathAndEvictsTheOldest,coding/piglet/signature/verifycache_test.go::TestCheckCached_RememberFailureKeepsTheResultand 7 more in docs/bug-ledger.json - Release note: After
pig piglet updateinstalls a new release of a pulled Piglet Binary,pig piglet listshows the Piglet once andpig piglet show <name>lists every installed release, including the one the update installed. Before, each release of a Piglet without a source was listed as its own Piglet andshowprinted only the first.
BF-73a09eb
fix: the ANSI renderer that replaces a frontend surface draws on the options terminal, not the surface's…
- Source: PiG's own defect
- Symptom: fix: the ANSI renderer that replaces a frontend surface draws on the options terminal, not the surface's terminal
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
73a09ebde9(2026-10-09) - Tests:
internal/codingagent/interactive_frontend_program_status_test.go::TestProcessTerminalShowsTheProgramStatusOnceNoSessionDraws
BF-73a6c8c
OpenAI-compatible chat completions requests keep the tool-call ids of the same model's earlier calls as they…
- Source: PiG's own defect
- Symptom: Every id in the request was rewritten, including the model's own.
- Root cause: openai-completions.ts convertMessages gives normalizeToolCallId to transformMessages, which applies it to a tool call of another model (transform-messages.ts:136-139) and maps its result to the new id. PiG instead rewrote every id in the converted request messages, so a same-model call with a '|' id or, on provider openai, an id over 40 characters was changed where Pi sends it as is. convertTranscript now passes the normalizer to transformMessages and the post-pass is gone.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
73a6c8c24a(2026-10-10) - Tests:
ai/openai_completions_tool_call_id_test.go::TestCompletionsNormalizesOnlyTheToolCallIDsOfOtherModels,ai/openai_completions_tool_call_id_test.go::TestCompletionsTruncatesAForeignPlainToolCallIDOnlyOnTheOpenAIProvider,ai/openai_test.go::TestConvertMessagesNormalizesToolCallIDsConsistently - Release note: OpenAI-compatible chat completions requests keep the tool-call ids of the same model's earlier calls as they were sent. As in Pi, only a tool call of another model is normalized: a
|-separated id is shortened, and on theopenaiprovider an id over 40 characters is truncated. Before, every id in the request was rewritten, including the model's own.
BF-73ef1ea
Reduced context-limit request failures by estimating input at 3.5 characters per token instead of 4 when…
- Source: PiG's own defect
- Symptom: Reduced context-limit request failures by estimating input at 3.5 characters per token instead of 4 when calculating output limits.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
73ef1ea5d7(2026-10-08) - Tests:
ai/estimate_content_test.go::TestEstimateTextAndImageContentTokensLikeUpstream,coding/session_tool_modifiers_port_test.go::TestInvalidToolModifierOptionsPort,coding/session_tool_modifiers_port_test.go::TestToolModifierOptionsChangeTheDefaultSelectionPort,coding/session_tool_modifiers_port_test.go::TestToolModifierOptionsSurviveReloadPort,internal/codingagent/tool_modifiers_test.go::TestApplyToolModifiersMatchesUpstream,internal/codingagent/tool_modifiers_test.go::TestGetToolListErrorMatchesUpstream - Release note: Reduced context-limit request failures by estimating input at 3.5 characters per token instead of 4 when calculating output limits.
BF-7472171
An extension dialog (select, confirm, input, editor) that opens while another is shown now replaces…
- Source: PiG's own defect
- Symptom: Previously the second dialog failed with "another extension dialog is already open" or waited for the first to close.
- Root cause: The UI bridge held the terminal focus token for the whole of a ui.select/confirm/input/editor call, so a second dialog from a subprocess extension waited until the first closed and the interactive displacement () never ran. A dialog now waits only while a focused custom component holds the terminal and holds no focus itself.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
74721716a1(2026-10-13),ef0d4cb621(2026-10-13) - Tests:
coding/extension/host/subprocess/ui_bridge_test.go::TestInteractiveDialogsDoNotWaitForEachOther,test/parity/scenarios/extensions-runtime/83-extension-dialog-displacement.toml - Release note: An extension dialog (
select,confirm,input,editor) that opens while another is shown now replaces it, as in Pi. This includes dialogs from subprocess extensions. The replaced dialog stays pending until its signal aborts it. Previously the second dialog failed with "another extension dialog is already open" or waited for the first to close.
BF-7489735
pig -e <directory> now loads a Node extension whose entry file is index.mjs and that has no…
- Source: PiG's own defect
- Symptom:
pig -e <directory>now loads a Node extension whose entry file isindex.mjsand that has nopackage.json,index.tsorindex.js, as Pi does. - Root cause: A directory with no package manifest and no index.ts or index.js is itself the extension path in Pi (package-manager.ts:1379-1384), and the loader's jiti import of it loads index.mjs. pig failed with "cannot resolve node extension entrypoint". resolveNodeEntrypoint and hasNodeSource now fall back to extsource.NodeDirectoryImport. Regression: TestResolveNodeEntrypointImportsAnIndexMjsDirectoryAsJiti (fails with the fallback removed).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0f6fdc2093(2026-10-07),7489735c3f(2026-10-09) - Tests:
coding/cli/package_update_workers_test.go::TestPackageUpdateChecksBoundWorkers,coding/extension/host/subprocess/builder_node_entry_test.go::TestResolveNodeEntrypointImportsAnIndexMjsDirectoryAsJiti - Release note:
pig -e <directory>now loads a Node extension whose entry file isindex.mjsand that has nopackage.json,index.tsorindex.js, as Pi does.
BF-74bc69b
fix(rpc): every Session replacement aborts the compactions of its batch, and an abort before the request…
- Source: PiG's own defect
- Symptom: fix(rpc): every Session replacement aborts the compactions of its batch, and an abort before the request answers with the abort error
- Root cause:
- the replacement aborts the batch's compact commands when it is dispatched and when it starts; a compact does not start before the batch drained; the abort reaches a compaction before it claims the Session (coding.WithCompactionAbort) - an abort before the summary request rejects it with the abort error itself, as pi-ai does (compaction.ErrRequestAborted); an abort during the request still answers Compaction cancelled - Pi's TypeError for the first of two aborted compacts and its answer order are recorded in D148 as defects not reproduced
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
74bc69bea4(2026-10-10) - Tests:
coding/cli/rpc_replacement_order_test.go::TestRPCReplacementAbortsTheCompactionOfItsBatch,coding/cli/rpc_replacement_order_test.go::TestRPCReplacementKeepsTheNothingToCompactAnswer,coding/session_test.go::TestCompactCancelsSynchronouslyFromStartEvent
BF-74e801f
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: Both shapes and the test generator now follow the two encoder structs; TestMemberScanAcceptsMessagesPiWrites gains openai-completions' responseId, responseModel, rawStopReason, errorMessage line (openai-completions.ts:564-586).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
74e801f982(2026-10-10) - Tests:
agent/message_shape_test.go::TestMemberScanAcceptsMessagesPiWrites,agent/message_shape_test.go::TestMemberScanAcceptsOnlyTextTheFieldsEncodeTo
BF-74f806c
fix(agent): end the tool flow at custom user messages after /tree navigation (#203)
- Source: PiG's own defect
- Symptom: fix(agent): end the tool flow at custom user messages after /tree navigation (#203)
- Root cause: Custom messages that become user messages (branch and compaction summaries) now end an open tool flow, and system messages that arrive while tool calls are pending are held until after the results, as Pi does. Fixes an Anthropic HTTP 400 for an orphaned tool_result after /tree navigation.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
74f806c799(2026-10-10, #203) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,coding/session_tree_wire_oracle_test.go::TestNavigateTreeEveryPointSendsValidToolFlow
BF-74fb98c
Markdown list items lex their text line by line like Pi: an HTML block starts at any item line, and an item…
- Source: PiG's own defect
- Symptom: Markdown list items lex their text line by line like Pi: an HTML block starts at any item line, and an item line indented four or more spaces past the content joins the text before it (so a heading, quote, rule, fence, table or bullet there stays text) and leaves an empty row before the text that follows.
- Root cause: Pi lexes a list item's text with top false: no paragraph rule, so an HTML block (including a custom tag, comment or pre) starts at any item line indented up to three spaces, and a line indented four or more past the item's content is indented code that blockTokens joins to the text before it with four spaces removed, plus a newline that leaves an empty row before the text that follows. Pig kept the indentation of an html line, and re-lexed a joined line as a heading, quote, rule, fence, table, bullet or setext underline.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
74fb98c362(2026-10-11) - Tests:
tui/markdown_list_item_lines_oracle_test.go::TestMarkdownListItemContinuationLinesMatchPi - Release note: Markdown list items lex their text line by line like Pi: an HTML block starts at any item line, and an item line indented four or more spaces past the content joins the text before it (so a heading, quote, rule, fence, table or bullet there stays text) and leaves an empty row before the text that follows.
BF-75426aa
The /login and /logout provider picker and the /tree selector show the terminal cursor in their search…
- Source: PiG's own defect
- Symptom: The
/loginand/logoutprovider picker and the/treeselector show the terminal cursor in their search and label inputs only while they hold focus, as Pi does. - Root cause: OAuthSelectorComponent.SetFocused propagates to the search input and TreeSelectorComponent.SetFocused to the active label input, which inherits the flag when it opens (oauth-selector.ts:63, tree-selector.ts:1349,1410). The search input no longer claims focus at construction. The editor-slot runners for /login and /tree now focus the selector and restore the previous focus, as showSelector does; the cursor marker follows focus. SetFocused takes the container lock so it does not race a render.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ed05099398(2026-10-06),75426aa52d(2026-10-07) - Tests:
ai/pi_messages_event_type_test.go::TestPiMessagesEventTypeConstantsMatchTheWireLiterals,coding/extension/session_boundary_draft_type_test.go::TestSessionBoundaryDraftTypeSelectsTheUpstreamMember,internal/codingagent/interactive_resume_thinking_test.go::TestInteractiveModeSetHiddenThinkingLabelReachesExistingAndLaterBlocks,internal/codingagent/reload_trust_models_test.go::TestInitNoticesFollowUpstreamOrder,internal/codingagent/selector_focus_test.go::TestEditorSlotOAuthSelectorTakesAndReturnsFocus,internal/codingagent/selector_focus_test.go::TestEditorSlotTreeSelectorTakesAndReturnsFocus,tui/assistant_message_block_label_test.go::TestAssistantMessageComponentSetHiddenThinkingLabel,tui/assistant_message_block_test.go::TestAssistantMessageBlock_SetHideThinkingBlockand 10 more in docs/bug-ledger.json - Release note: The
/loginand/logoutprovider picker and the/treeselector show the terminal cursor in their search and label inputs only while they hold focus, as Pi does. The picker's search input claimed focus even when it had none.
BF-75426aa-2
A Node extension's tool_call handler can edit event.input in place and the tool runs with the edit, as in…
- Source: PiG's own defect
- Symptom: A Node extension's
tool_callhandler can editevent.inputin place and the tool runs with the edit, as in Pi: the host rewrites the call's input in place, and assigning a new object toevent.inputis ignored, as in Pi. - Root cause: OAuthSelectorComponent.SetFocused propagates to the search input and TreeSelectorComponent.SetFocused to the active label input, which inherits the flag when it opens (oauth-selector.ts:63, tree-selector.ts:1349,1410). The search input no longer claims focus at construction. The editor-slot runners for /login and /tree now focus the selector and restore the previous focus, as showSelector does; the cursor marker follows focus. SetFocused takes the container lock so it does not race a render.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ed05099398(2026-10-06),75426aa52d(2026-10-07) - Tests:
ai/pi_messages_event_type_test.go::TestPiMessagesEventTypeConstantsMatchTheWireLiterals,coding/extension/session_boundary_draft_type_test.go::TestSessionBoundaryDraftTypeSelectsTheUpstreamMember,internal/codingagent/interactive_resume_thinking_test.go::TestInteractiveModeSetHiddenThinkingLabelReachesExistingAndLaterBlocks,internal/codingagent/reload_trust_models_test.go::TestInitNoticesFollowUpstreamOrder,internal/codingagent/selector_focus_test.go::TestEditorSlotOAuthSelectorTakesAndReturnsFocus,internal/codingagent/selector_focus_test.go::TestEditorSlotTreeSelectorTakesAndReturnsFocus,tui/assistant_message_block_label_test.go::TestAssistantMessageComponentSetHiddenThinkingLabel,tui/assistant_message_block_test.go::TestAssistantMessageBlock_SetHideThinkingBlockand 10 more in docs/bug-ledger.json - Release note: A Node extension's
tool_callhandler can editevent.inputin place and the tool runs with the edit, as in Pi: the host rewrites the call's input in place, and assigning a new object toevent.inputis ignored, as in Pi. The Go, Python and Rust SDKs follow the same rule, and atool_callreply in an unexpected shape now blocks the call (#165).
BF-756cac8
A streaming run no longer hangs, rarely and mostly under load, when a response turn resumes just as the drain…
- Source: PiG's own defect
- Symptom: A streaming run no longer hangs, rarely and mostly under load, when a response turn resumes just as the drain that granted it ends: a pausing coroutine put back a drain flag the ending drain had already cleared, which left every goroutine asleep with a reaction still queued.
- Root cause: settle saved the executor's draining flag at entry and wrote it back when the body went on to pause. For a body that a serving goroutine claimed (needsDriver) the flag belongs to the drain that granted it, live on another goroutine and about to end; when that end landed in settle's unlocked reaction window, the write-back set draining again with no loop on any stack. serve and startDrainLocked then refused to drain, and the queued reactions never ran (a lost wakeup seen as a hang of TestGoogleVertexProviderStreamMatchesPi under -race).
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
756cac8180(2026-10-14),a07977e2b9(2026-10-14) - Tests:
ai/continuation_settle_drain_test.go::TestContinuationSettleDoesNotResurrectEndedDrain,ai/continuation_settle_drain_test.go::TestContinuationSettleDoesNotRunSecondLoopForTurnGrantedFromSettle,ai/google_vertex_stream_oracle_test.go::TestGoogleVertexProviderStreamMatchesPi - Release note: A streaming run no longer hangs, rarely and mostly under load, when a response turn resumes just as the drain that granted it ends: a pausing coroutine put back a drain flag the ending drain had already cleared, which left every goroutine asleep with a reaction still queued.
BF-75c6ccb
fix(codingagent): context edits leave system messages unchanged
- Source: PiG's own defect
- Symptom: fix(codingagent): context edits leave system messages unchanged
- Root cause: 400 random entry trees against Pi's buildSessionContext (TestBuildSessionContextMatchesPi): projectContextEntry maps only user/assistant/toolResult/custom messages; replaceContextContent also rewrote a system message.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
75c6ccb80e(2026-10-10) - Tests:
internal/codingagent/session_context_oracle_test.go::TestBuildSessionContextMatchesPi
BF-7633906
On arm64, tool_search and searchTools() scores use the same natural logarithm as Pi running on Node for…
- Source: PiG's own defect
- Symptom: The logarithm in the BM25 weights differed from Node's in the last bit for about 1 in 500 arguments, which could change scores and break ties in a different order.
- Root cause: Math.log is V8's fdlibm port in C++, not JavaScript arithmetic. Node's arm64 builds compile it with clang, whose default -ffp-contract=on fuses a product into the addition or subtraction of the same C expression (never across statements). Node 24.19 on darwin-arm64 matches that variant bit for bit over TestLogMatchesNode's 360022 inputs.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7633906d03(2026-10-13) - Tests:
internal/jsmath/log_oracle_test.go::TestLogMatchesNode - Release note: On arm64,
tool_searchandsearchTools()scores use the same natural logarithm as Pi running on Node for arm64, whose V8 math library fuses each multiply-add in one step. Before, the logarithm in the BM25 weights differed from Node's in the last bit for about 1 in 500 arguments, which could change scores and break ties in a different order.
BF-7662334
An extension that writes a large amount to stderr no longer grows its diagnostic log without limit
- Source: PiG's own defect
- Symptom: An extension that writes a large amount to stderr no longer grows its diagnostic log without limit.
- Root cause: Host wire (go-vs-rust-harness #10): an outbound frame is encoded into a pooled buffer holding the length prefix and written with one socket write; Send allocates nothing in steady state (was 1 object, 20 us -> 8.6 us). Every extension process's stderr goes through a log that keeps the first 1 MiB and the last 64 KiB and counts the bytes it dropped, so a flood neither blocks the pipe nor fills the disk.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
766233435b(2026-10-09) - Tests:
coding/extension/host/subprocess/conn_framing_test.go::TestConnAllocationBudgets,coding/extension/host/subprocess/conn_framing_test.go::TestLargeFrameChunksArriveIntact,coding/extension/host/subprocess/conn_framing_test.go::TestSendAllocatesNothingInSteadyState,coding/extension/host/subprocess/conn_framing_test.go::TestSmallFrameIsOneSocketWrite,coding/extension/host/subprocess/stderr_bound_test.go::TestFloodingChildNeitherBlocksNorFillsTheDisk,coding/extension/host/subprocess/stderr_bound_test.go::TestHelperFloodStderr,coding/extension/host/subprocess/stderr_bound_test.go::TestStderrLogKeepsHeadAndTail,coding/extension/host/subprocess/stderr_bound_test.go::TestStderrLogUnderTheBoundIsVerbatimand 4 more in docs/bug-ledger.json - Release note: An extension that writes a large amount to stderr no longer grows its diagnostic log without limit. The log keeps the first 1 MiB and the last 64 KiB of the output and says how many bytes it dropped, so a startup failure and a crash line both survive a flood.
BF-773a868
RPC mode no longer hangs when a Node extension command calls pi.sendMessage(..., { triggerTurn: true })…
- Source: PiG's own defect
- Symptom: RPC mode no longer hangs when a Node extension command calls
pi.sendMessage(..., { triggerTurn: true })while abefore_agent_starthandler is subscribed: the handler runs inside the call, as in Pi, and the run starts after the command's response. - Root cause: TestRPCCommandTurnStartOrderMatchesPi/async-followup hung (agent_settled timed out). An earlier change made a triggering pi.sendMessage a synchronous bridge call, so before_agent_start starts nested in that call's wait; its synchronous prefix ran (setStatus was written), but the handler's suspension was never reported, because the rest of it waits on the call's own answer. The host waited for the suspension, the runtime for the answer: a deadlock. A request started nested in a synchronous call now reports "blocked" (host_call) once its synchronous prefix returns, unless its handler reported the suspension already (session_info_changed, thinking_level_select).
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),773a868c84(2026-10-13) - Tests:
coding/cli/rpc_command_send_order_test.go::TestRPCCommandTurnStartOrderMatchesPi,coding/cli/rpc_ui_integration_test.go::TestRPCModeExtensionUIRoundTrip,coding/cli/session_wire_lifecycle_test.go::TestModeWireLifecycle,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.tomland 2 more in docs/bug-ledger.json - Release note: RPC mode no longer hangs when a Node extension command calls
pi.sendMessage(..., { triggerTurn: true })while abefore_agent_starthandler is subscribed: the handler runs inside the call, as in Pi, and the run starts after the command's response.
BF-773a868-2
RPC mode no longer hangs when a Node extension command calls pi.appendEntry and then pi.setSessionName…
- Source: PiG's own defect
- Symptom: RPC mode no longer hangs when a Node extension command calls
pi.appendEntryand thenpi.setSessionNamewhile asession_info_changedhandler is subscribed. - Root cause: TestRPCCommandTurnStartOrderMatchesPi/async-followup hung (agent_settled timed out). An earlier change made a triggering pi.sendMessage a synchronous bridge call, so before_agent_start starts nested in that call's wait; its synchronous prefix ran (setStatus was written), but the handler's suspension was never reported, because the rest of it waits on the call's own answer. The host waited for the suspension, the runtime for the answer: a deadlock. A request started nested in a synchronous call now reports "blocked" (host_call) once its synchronous prefix returns, unless its handler reported the suspension already (session_info_changed, thinking_level_select).
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
773a868c84(2026-10-13) - Tests:
coding/cli/rpc_command_send_order_test.go::TestRPCCommandTurnStartOrderMatchesPi,coding/cli/rpc_ui_integration_test.go::TestRPCModeExtensionUIRoundTrip,coding/cli/session_wire_lifecycle_test.go::TestModeWireLifecycle - Release note: RPC mode no longer hangs when a Node extension command calls
pi.appendEntryand thenpi.setSessionNamewhile asession_info_changedhandler is subscribed.
BF-7768bea
fix(ai): tool-call arguments are written as JSON.stringify writes them, and keep a lone surrogate
- Source: PiG's own defect
- Symptom: fix(ai): tool-call arguments are written as JSON.stringify writes them, and keep a lone surrogate
- Root cause: ToolCall.ArgumentsJSON, which every provider request (OpenAI-compatible, Anthropic, Mistral, Google), hook and the tree use for the arguments text, fell back to encoding/json when no member order was recorded, that is for every call whose objects were already in sorted order: it escaped <, >, &, U+2028 and U+2029, sorted integer-like keys as text, and turned a lone surrogate into three U+FFFD. It now walks the arguments the way the ordered path does. Decoding the arguments (SetArgumentsJSON, a streamed call, ParseJSONWithRepair, the clone an assistant message takes when it is observed) used encoding/json, so a model's lone surrogate escape became U+FFFD while the rest of the wire kept it…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
7768bea9d9(2026-10-10) - Tests:
agent/lone_surrogate_message_test.go::TestAgentMessageRoundTripKeepsALoneSurrogate,ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunk,ai/tool_call_arguments_test.go::TestArgumentsJSONIsJSONStringifyWithoutARecordedOrder,ai/tool_call_arguments_test.go::TestIsWTF8,ai/tool_call_arguments_test.go::TestOpenAIRequestReplaysArgumentsAsJSONStringify,ai/tool_call_arguments_test.go::TestStreamedArgumentsKeepALoneSurrogate,internal/codingagent/compaction/estimate_oracle_test.go::TestEstimateTokensMatchesPiOracle
BF-779318a
OpenAI-compatible streams where one chunk carries reasoning and content (LiteLLM and similar proxies) now…
- Source: PiG's own defect
- Symptom: OpenAI-compatible streams where one chunk carries reasoning and content (LiteLLM and similar proxies) now place the thinking block before the text.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
779318a6ea(2026-10-14, #10926) - Tests:
ai/openai_completions_member_type_oracle_test.go::TestOpenAICompletionsMistypedMembersMatchPi,ai/openai_reasoning_pending_test.go::TestParseSSE_ReasoningDetailsApplyBeforeNonIterableToolCalls,ai/openai_reasoning_pending_test.go::TestParseSSE_ThinkingBeforeTextWhenOneDeltaCarriesBoth,ai/openai_tool_choice_stream_upstream_test.go::TestOpenAICompletionsToolChoiceStreamUpstream - Release note: OpenAI-compatible streams where one chunk carries reasoning and content (LiteLLM and similar proxies) now place the thinking block before the text. Carried forward from Pi 63035734 (earendil-works/pi#10926), past the 1.2.0+3daa2028 pin.
BF-78c4c6c
pig piglet add now names the offending bundled path and where it came from (for example `Package herdr…
- Source: PiG's own defect
- Symptom:
pig piglet addnow names the offending bundled path and where it came from (for examplePackage herdr origin "local:../../components/herdr") and says it leaves the added subdirectory, instead of a bare "must be a portable relative path" error. - Root cause: Adding a Piglet from a monorepo subdirectory whose Packages point outside it failed with 'Piglet closure path must be a portable relative path'. The error now names the path, the origin it came from, and why it is rejected.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
78c4c6c647(2026-10-13) - Tests:
coding/piglet/monorepo_closure_test.go::TestBundledPigletClosureErrorNamesPathAndOrigin - Release note:
pig piglet addnow names the offending bundled path and where it came from (for examplePackage herdr origin "local:../../components/herdr") and says it leaves the added subdirectory, instead of a bare "must be a portable relative path" error.
BF-78f2d55
pig mcp login --help described --timeout as only the wait for the browser; it limits the whole sign-in…
- Source: A fix Pi shipped that PiG ported
- Symptom:
pig mcp login --helpdescribed--timeoutas only the wait for the browser; it limits the whole sign-in, as Pi 148622bc now says. - Root cause: Ports Pi packages/coding-agent/src/extensions/mcp/cli.ts HELP: the --timeout line reads "Time limit for the whole sign-in (default: 300)". The Go docs (docs/site/docs/mcp.md, internal/pigdocs/content/mcp.md) already describe --timeout as limiting the whole sign-in.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
78f2d55ded(2026-10-12) - Tests:
coding/cli/mcp_command_test.go::TestPigMcpHelpIsPisHelpWithPiGsIdentity,coding/mcpext/cli_oracle_test.go::TestMcpCommandMatchesPi,coding/mcpext/cli_test.go::TestMcpCommandHelpIsPisHelpWithPiGsIdentity - Release note:
pig mcp login --helpdescribed--timeoutas only the wait for the browser; it limits the whole sign-in, as Pi 148622bc now says.
BF-79eadcf
Long sessions no longer slow down each streamed frame in the main screen: a frame redraws from the first line…
- Source: PiG's own defect
- Symptom: Long sessions no longer slow down each streamed frame in the main screen: a frame redraws from the first line that changed instead of re-rendering and comparing the whole transcript, with the same terminal output.
- Root cause: The main screen asks the root for the lines kept since the revision it drew last. Line resets and the differential scan start at the first line that can differ: the earlier of the kept prefix and both frames' cursor rows. The per-line reuse of earlier reset results stays, but only while the previous input array is intact: it was composed with overlays, or the root has rendered at most once since. Overlay frames keep nothing, and a frame after RestoreRenderState scans everything. Mouse dispatch reads child heights from the window.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
79eadcf623(2026-10-10) - Tests:
tui/complexity_scaling_norace_test.go::TestScalingMainScreenFrames,tui/container_render_cache_test.go::TestMainScreenCursorExtractionDoesNotMutateBorrowedContainerLines,tui/windowed_render_test.go::TestWindowedFramesEqualWholeFrames - Release note: Long sessions no longer slow down each streamed frame in the main screen: a frame redraws from the first line that changed instead of re-rendering and comparing the whole transcript, with the same terminal output.
BF-79f9a9e
fix(agent): convert a live partial with one clone and keep events at an unchanged publication (TU-T7 rest)
- Source: PiG's own defect
- Symptom: fix(agent): convert a live partial with one clone and keep events at an unchanged publication (TU-T7 rest)
- Root cause: shallowAssistantMessageFrom takes the fields of the shallow copy it builds from the capture instead of observing the capture a second time, so a message_update clones the partial once.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
79f9a9eeb8(2026-10-13) - Tests:
ai/assistant_published_clone_test.go::TestObservedStreamingToolCallEqualsFullClone
BF-7a4a8e8
fix duplicate jsstring import in tui/assistant_message_block.go
- Source: PiG's own defect
- Symptom: fix duplicate jsstring import in tui/assistant_message_block.go
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
7a4a8e8749(2026-10-11) - Tests: None recorded.
BF-7b0f5d7
A native extension provider's filterAllModels receives the models its getAllModels (or getModels)…
- Source: PiG's own defect
- Symptom: A native extension provider's
filterAllModelsreceives the models itsgetAllModels(orgetModels) returned, with their members in the order the provider wrote them, instead of copies with members sorted by name. - Root cause: models.ts:731-734 getAllAvailable passes filterAllModels
provider.getAllModels?.() ?? provider.getModels(), the provider's own objects. refreshNativeProvider rebuilt them from ProviderModelConfig through a map[string]json.RawMessage, so a subprocess extension's filterAllModels saw every model's members sorted by name. The list the provider returned now goes through as it is; a provider with neither getter still has its registered configs read as models, with provider after the config's members as{ ...definition, provider }(provider-composer.ts:297). - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7b0f5d7da8(2026-10-13) - Tests:
internal/codingagent/native_provider_filter_order_test.go::TestNativeProviderFilterAllModelsSeesTheProvidersOwnModels - Release note: A native extension provider's
filterAllModelsreceives the models itsgetAllModels(orgetModels) returned, with their members in the order the provider wrote them, instead of copies with members sorted by name.
BF-7b59fe2
pig piglet build --format binary from a Pig source directory that is not a Git checkout (for example…
- Source: PiG's own defect
- Symptom:
pig piglet build --format binaryfrom a Pig source directory that is not a Git checkout (for examplePIG_SOURCE_ROOTpointing at an extracted archive) now says the directory must be a Git checkout of PiG, names it, and suggests cloning PiG or unsettingPIG_SOURCE_ROOT, instead of failing withresolve Pig source revision: exit status 128. - Root cause: A Piglet Binary build from a plain copy of the PiG source failed with 'resolve Pig source revision: exit status 128'. The error now names the directory, says it must be a Git checkout and how to fix it; other git failures keep git's stderr line.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
375de4a98c(2026-10-12),35006c8baf(2026-10-13),7b59fe2605(2026-10-13) - Tests:
coding/cli/strip_providers_binary_test.go::TestStripBedrockConverseStreamBuildOmitsAWSSDK,coding/pigletbuild/source_fetch_test.go::TestSourceRevisionOutsideGitCheckoutNamesDirectory,internal/codingagent/program_status_reporter_oracle_test.go::TestProgramStatusReporterRandomSequencesMatchPi,third_party/awssdk/internal/endpoints/v2/lazy_test.go::TestLazyRegexpMatchesLikeRegexp - Release note:
pig piglet build --format binaryfrom a Pig source directory that is not a Git checkout (for examplePIG_SOURCE_ROOTpointing at an extracted archive) now says the directory must be a Git checkout of PiG, names it, and suggests cloning PiG or unsettingPIG_SOURCE_ROOT, instead of failing withresolve Pig source revision: exit status 128. Other git failures now include git's error line.
BF-7b92429
find and grep trim like JavaScript: FindTool trimmed each fd output line with strings.TrimSpace where…
- Source: PiG's own defect
- Symptom:
findandgreptrim like JavaScript:FindTooltrimmed each fd output line withstrings.TrimSpacewhere find.ts:270 usesString.prototype.trim, so a file whose name ends with U+FEFF lost nothing in Pi but a name ending in U+0085 lost it in PiG (and the reverse). - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),7b92429a25(2026-10-11),a2755058da(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/discover_extensions_oracle_test.go::TestDiscoverExtensionPathsMatchPi,internal/codingagent/armin_test.go::TestArminFramesMatchPinnedPi,internal/codingagent/custom_message_components_oracle_test.go::TestCustomMessageAndEntryComponentsMatchPi,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundariesand 9 more in docs/bug-ledger.json - Release note:
findandgreptrim like JavaScript:FindTooltrimmed each fd output line withstrings.TrimSpacewhere find.ts:270 usesString.prototype.trim, so a file whose name ends with U+FEFF lost nothing in Pi but a name ending in U+0085 lost it in PiG (and the reverse).find's lines and stderr message andgrep's stderr message and blank-line check now usejsstring.Trim(find.ts:252,270; grep.ts:220,252). New live-Pi oracleTestFindTrimsOutputLinesLikeJavaScript(internal/codingagent/tools/find_trim_names_oracle_test.go: names beginning or ending with U+FEFF, U+0085, U+00A0 or a space, and names that are only U+FEFF or U+0085); red before the fix (3 of 7 directories), green after;./internal/codingagent/tools -racegreen. The stderr and ripgrep blank-line trims are not reachable by the oracle (they need fd or rg to fail or emit a blank JSON line) and are changed for the same rule only.
BF-7ba3211
A resumed session no longer holds its whole file in memory
- Source: PiG's own defect
- Symptom: A resumed session no longer holds its whole file in memory.
- Root cause: The range is now 225-285 MB.
- User impact: Behavior differed from Pi
- Platform: macOS
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
7ba3211bdc(2026-10-11) - Tests: None recorded.
- Release note: A resumed session no longer holds its whole file in memory. PiG keeps a small index of every entry and decodes only what the active context needs (the latest compaction's kept messages and everything after them); older entries are read back from the session file when a command such as
/tree,/forkor export needs them. On a real 314 MB session, an interactive resume measured 150-190 MB after loading and 225-285 MB after opening/tree(two profiling runs on macOS), where PiG 0.4.1 took 1.2-1.3 GB and Pi 1.1.0 about 700 MB. History outside the active context is not kept decoded, so a long-open session file does not accumulate it in memory.
BF-7c59949
fix(codingagent): session selector keeps Pi's out-of-range selected index
- Source: PiG's own defect
- Symptom: fix(codingagent): session selector keeps Pi's out-of-range selected index
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
7c599493e4(2026-10-10) - Tests:
internal/codingagent/session_selector_oracle_test.go::TestSessionSelectorInputMatchesPi
BF-7cca5ee
A Node extension can default-import TypeBox (import Type from "typebox", and likewise typebox/value…
- Source: PiG's own defect
- Symptom: A Node extension can default-import TypeBox (
import Type from "typebox", and likewisetypebox/value,typebox/compileand@sinclair/typebox*), as it can in Pi. - Root cause:
- runner.ts returns to partial: Pi renders before_agent_start's event.systemPrompt and ctx.getSystemPrompt() from the shared options on every read (runner.ts:1421-1438); Pig hands each handler the pre-edit string. Probed on the pinned Pi runner. - TestNodeRuntimeVirtualModulesMatchPiNamespaces read Pi's namespaces through Pig's loader hooks and compared Pig with itself; it now reads them in a hook-free process. - That comparison found the TypeBox shims had no default export; vendor-typebox.sh re-exports it, and TestTypeBoxDefaultImportsLoadInANodeExtension loads such an extension through the host.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7cca5ee58a(2026-10-06) - Tests:
coding/extension/host/subprocess/node_loader_test.go::TestNodeRuntimeVirtualModulesMatchPiNamespaces,coding/extension/host/subprocess/upstream_tool_schema_test.go::TestTypeBoxDefaultImportsLoadInANodeExtension - Release note: A Node extension can default-import TypeBox (
import Type from "typebox", and likewisetypebox/value,typebox/compileand@sinclair/typebox*), as it can in Pi. The import failed to link in an.mjsextension and returned the wrong object in a.tsextension.
BF-7d0621f
fix(extension): keep Pi's three-parameter EmitBeforeAgentStart (prompt, images, options); a custom run's…
- Source: PiG's own defect
- Symptom: fix(extension): keep Pi's three-parameter EmitBeforeAgentStart (prompt, images, options); a custom run's trigger travels on ctx
- Root cause: Clears the S4 signature-mismatch gaps (ExtensionRunner.emitBeforeAgentStart). Ledger regenerated; interface-gaps: 0 gaps.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
7d0621f995(2026-10-09) - Tests: None recorded.
BF-7dc9b09
-e <local Package> now contributes its prompts, skills and themes, loaded before the project, user and…
- Source: PiG's own defect
- Symptom:
-e <local Package>now contributes its prompts, skills and themes, loaded before the project, user and Package resources, so they win same-name collisions as in Pi. - Root cause: A -e local Package contributed only extensions. Pi's resource loader resolves the -e sources as temporary Packages and merges their prompts, skills and themes before the project, user and Package resources (cliEnabledPrompts/Skills/Themes), so they win same-name collisions and survive --no-prompt-templates, --no-skills and --no-themes.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7dc9b09ef7(2026-10-09),da99854d1b(2026-10-09) - Tests:
coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,test/parity/scenarios/extensions-runtime/76-cli-package-resources.toml - Release note:
-e <local Package>now contributes its prompts, skills and themes, loaded before the project, user and Package resources, so they win same-name collisions as in Pi. They survive--no-prompt-templates,--no-skillsand--no-themes. A directory with anextensions,skills,promptsorthemesdirectory is a Package and no longer fails to load as an extension.
BF-7dc9b09-2
--prompt-template and --theme paths are now merged after the project, user and Package resources, as in…
- Source: PiG's own defect
- Symptom:
--prompt-templateand--themepaths are now merged after the project, user and Package resources, as in Pi, so a same-name resource there wins over them. - Root cause: A -e local Package contributed only extensions. Pi's resource loader resolves the -e sources as temporary Packages and merges their prompts, skills and themes before the project, user and Package resources (cliEnabledPrompts/Skills/Themes), so they win same-name collisions and survive --no-prompt-templates, --no-skills and --no-themes.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7dc9b09ef7(2026-10-09) - Tests:
test/parity/scenarios/extensions-runtime/76-cli-package-resources.toml - Release note:
--prompt-templateand--themepaths are now merged after the project, user and Package resources, as in Pi, so a same-name resource there wins over them.
BF-7e09ec0
fix(codingagent): the idle collection runs one forced GC cycle (D172)
- Source: PiG's own defect
- Symptom: fix(codingagent): the idle collection runs one forced GC cycle (D172)
- Root cause: debug.FreeOSMemory forces a collection itself, so the runtime.GC() before it was a second full cycle that returned nothing more.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
7e09ec03c8(2026-10-13) - Tests: None recorded.
BF-7e1a05c
Resource resolution follows Pi's DefaultPackageManager.resolve in two cases: built-in extensions come after…
- Source: PiG's own defect
- Symptom: Resource resolution follows Pi's
DefaultPackageManager.resolvein two cases: built-in extensions come after file and package extensions, and a Package listed twice in the same project settings uses its last entry (and its filters), keeping the first position. - Root cause: TestLoadMcpConfigMatchesPiOnRandomFiles loads 1500 random global and project mcp.json files through Pi's loadMcpConfig and PiG's LoadMcpConfig: 187 differed, among them every file with a numeric server name, because Object.entries lists array-index keys first and orderedjson.Object kept file order. Object.Set now inserts an array-index key at its ascending place before the other keys (Keys, MarshalJSON, Clone follow), with TestObjectEnumeratesArrayIndexKeysFirst. Dependent packages (codingagent, extension, mcpext, mcp, packagecontent) pass; the failures left in tools and the node-backed subprocess tests are the fd/rg and node-shim environment ones.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
12cd60837e(2026-10-09),24232ef878(2026-10-09),2b3403045b(2026-10-09),3def2a7bd8(2026-10-10),7e1a05ccfd(2026-10-10),919575a79c(2026-10-10),f3b4045e8a(2026-10-10) - Tests:
coding/mcpext/config_load_oracle_test.go::TestLoadMcpConfigMatchesPiOnRandomFiles,internal/codingagent/paths_oracle_test.go::TestPathsMatchPi,internal/codingagent/session_file_bytes_oracle_test.go::TestSessionFileBytesMatchPi,internal/codingagent/settings_file_bytes_oracle_test.go::TestSettingsFileBytesMatchPi,internal/codingagent/settings_load_oracle_test.go::TestSettingsLoadMatchesPiOnCorruptedFiles,internal/ignorerules/ignore_oracle_test.go::TestIgnoredMatchesPiIgnorePackage,internal/ignorerules/ignorerules_test.go::TestIgnoreRulesFollowTheIgnorePackage,internal/jsonparse/json_parse_oracle_test.go::TestValidateMatchesNodeJSONParseand 2 more in docs/bug-ledger.json - Release note: Resource resolution follows Pi's
DefaultPackageManager.resolvein two cases: built-in extensions come after file and package extensions, and a Package listed twice in the same project settings uses its last entry (and its filters), keeping the first position.
BF-7ea21f3
RPC output writes summed usage (a tool result's usage with nested model calls, a compaction summary's usage)…
- Source: PiG's own defect
- Symptom: RPC output writes summed usage (a tool result's usage with nested model calls, a compaction summary's usage) with
cacheWrite1handreasoningbeforetotalTokens, as Pi'scombineUsagecreates them, instead of aftercost. - Root cause: Tests: - TestRPCCombinedUsageKeepsTheLiteralOrder. It also checks that a decoded Anthropic usage keeps the after-cost order. - TestRPCMessageUpdateStreamingVariantBytesMatchPi now decodes the same usage Pi's side holds instead of building a Go literal, which cannot say where the splits were created. - TestAssistantLateMembersFollowPiCreationOrder gains "anthropic cacheWrite1h from message_start only". The existing Anthropic case could not catch a missing splitsAfterCost at message_start, because its message_delta set the flag again. Removing the message_start assignment (anthropic.go) now fails this case. Pi creates cacheWrite1h there with
|| 0(anthropic-messages.ts:684) on every stream. - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),2309604d80(2026-10-10),7ea21f3e44(2026-10-10),c10cde302d(2026-10-10),eeac307095(2026-10-10),f29e906cb3(2026-10-10) - Tests:
agent/assistant_member_order_oracle_test.go::TestAssistantLateMembersFollowPiCreationOrder,agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowClosesand 11 more in docs/bug-ledger.json - Release note: RPC output writes summed usage (a tool result's usage with nested model calls, a compaction summary's usage) with
cacheWrite1handreasoningbeforetotalTokens, as Pi'scombineUsagecreates them, instead of aftercost.
BF-7f3a71c
fix(durable): the reference Harness wraps a plain Models once too
- Source: PiG's own defect
- Symptom: fix(durable): the reference Harness wraps a plain Models once too
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
7f3a71c014(2026-10-21) - Tests: None recorded.
BF-7f441d7
MCP OAuth URLs are WHATWG URLs, as in Pi
- Source: PiG's own defect
- Symptom: MCP OAuth URLs are WHATWG URLs, as in Pi.
- Root cause: Tests: - TestOAuthURLsMatchPi runs Pi's own packages/mcp/src/oauth source from the upstream mirror on 21 cases. On the old code 15 of the first 20 differed: fragments in authorize (6), token (3) and challenge (3) URLs, the opaque base (1) and the null origins (2). - TestDiscoveryRequestsTheURLWithoutItsFragment guards the new request path. It was not red before the change, since *url.URL also dropped the fragment.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7f441d7364(2026-10-10),827b3cffa4(2026-10-10),ced3a9c4d4(2026-10-10) - Tests:
coding/cli/rpc_model_type_binary_test.go::TestRPCModelObjectsCarryTheRegistryModelType,coding/cli/rpc_model_type_test.go::TestRPCBuiltinChatModelCarriesType,coding/cli/rpc_model_type_test.go::TestRPCModelProjectionCarriesModelType,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,internal/testenv/spawn_test.go::TestCleanupStopsARunningChild,internal/testenv/spawn_test.go::TestOutputRefusesAStreamTheCallerSet,internal/testenv/spawn_test.go::TestStopEndsAChildThatIgnoresSIGTERM,internal/testenv/spawn_test.go::TestWaitDoesNotBlockOnStdoutHeldByADescendantand 5 more in docs/bug-ledger.json - Release note: MCP OAuth URLs are WHATWG URLs, as in Pi. An empty fragment (
#) or a malformed fragment escape now stays in the authorization URL that is shown and opened, and in the insecure-endpoint error. The authorization parameters replace repeated names, and an opaque authorization server URL fails withInvalid URL. The protected-resource check compares origins, which arenullfor custom schemes.
BF-7fc4ef4
fix(coding): shut down the extension host of a DefaultResourceLoader an extension constructed when its last…
- Source: PiG's own defect
- Symptom: fix(coding): shut down the extension host of a DefaultResourceLoader an extension constructed when its last holder lets go
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
7fc4ef463d(2026-10-21) - Tests: None recorded.
BF-8001cc5
SettingsManager collection getters (getPackages, getExtensionPaths, getSkillPaths, getPromptTemplatePaths…
- Source: PiG's own defect
- Symptom:
SettingsManagercollection getters (getPackages, getExtensionPaths, getSkillPaths, getPromptTemplatePaths, getThemePaths, getAllModelThinkingLevels, getThinkingBudgets, getTerminalCapabilityOverrides, getWarnings, getHttpIdleTimeoutMs, getWebSocketConnectTimeoutMs, getExternalEditorCommand): new live-Pi oracleTestSettingsManagerCollectionGettersMatchPi(2,500 layered global/project file pairs; throws compared by message). - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),e8cc072521(2026-10-08),433d232815(2026-10-09),8001cc51af(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunkand 6 more in docs/bug-ledger.json - Release note:
SettingsManagercollection getters (getPackages, getExtensionPaths, getSkillPaths, getPromptTemplatePaths, getThemePaths, getAllModelThinkingLevels, getThinkingBudgets, getTerminalCapabilityOverrides, getWarnings, getHttpIdleTimeoutMs, getWebSocketConnectTimeoutMs, getExternalEditorCommand): new live-Pi oracleTestSettingsManagerCollectionGettersMatchPi(2,500 layered global/project file pairs; throws compared by message). Two real fixes: (1) an authored"httpIdleTimeoutMs": nullwas dropped (Pi keeps it, overrides the other layer, and getHttpIdleTimeoutMs throws "Invalid httpIdleTimeoutMs setting: null"; websocketConnectTimeoutMs already did this); (2) a project"externalEditor": ""did not override a global editor (Pi's merge keeps the empty string, so the editor falls back to VISUAL, EDITOR, nano); both now survive load, merge and encode. RegressionTestSettingsEncodeKeepsAnAuthoredNullHTTPIdleTimeoutAndEmptyExternalEditor. 11 mutations red. Not compared: wrong-typed array members, fractional numbers, a thrown value that is neither number nor string; getWarnings is compared as the consumer reads it (anthropicExtraUsage !== false).
BF-8001cc5-2
SettingsManager.getCompactionSettings (+ getCompactionReserveTokens, getCompactionKeepRecentTokens)…
- Source: PiG's own defect
- Symptom:
SettingsManager.getCompactionSettings(+getCompactionReserveTokens,getCompactionKeepRecentTokens),getShellPath,getSessionDir,getThemeSetting,getTheme,getModelThinkingLevel: new live-Pi oracleTestSettingsManagerResolvedGettersMatchPi(3,000 layered global/project file pairs; model override beats ordinary setting beats default; invalid authored token values and unsafe integers compared by thrown message; path settings through normalizePath with home expansion and file URLs; a theme containing "/" reads as unset). - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),e8cc072521(2026-10-08),433d232815(2026-10-09),8001cc51af(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunkand 7 more in docs/bug-ledger.json - Release note:
SettingsManager.getCompactionSettings(+getCompactionReserveTokens,getCompactionKeepRecentTokens),getShellPath,getSessionDir,getThemeSetting,getTheme,getModelThinkingLevel: new live-Pi oracleTestSettingsManagerResolvedGettersMatchPi(3,000 layered global/project file pairs; model override beats ordinary setting beats default; invalid authored token values and unsafe integers compared by thrown message; path settings through normalizePath with home expansion and file URLs; a theme containing "/" reads as unset). No bug found. 9 mutations red (override ignored, either validation removed, override key, object message, theme slash rule, normalizePath bypass, model-level key). Not compared: wrong-typed values, empty strings.
BF-801cadd
fix(codingagent): session files decode invalid UTF-8 as Node does
- Source: PiG's own defect
- Symptom: fix(codingagent): session files decode invalid UTF-8 as Node does
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
801cadd5a9(2026-10-10) - Tests:
internal/codingagent/session_load_oracle_test.go::TestLoadEntriesFromFileMatchesPiOnCorruptedFiles
BF-8048c85
Tool-call header argument preview no longer splits a character: truncateArg (tui/tool_execution.go) cut…
- Source: PiG's own defect
- Symptom: Tool-call header argument preview no longer splits a character:
truncateArg(tui/tool_execution.go) cut tomax-1bytes, so a non-ASCII argument longer than the budget ended in a half character that the JSON quoting turned into a visible U+FFFD before the ellipsis (text:"ééé…é�…"); it now backs up to the start of a character. - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),8048c851e3(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,test/longsession/session_oracle_test.go::TestSessionManagerMatchesPi,test/parity/scenarios/rpc/58-rpc-session-wrong-typed-members.tomland 1 more in docs/bug-ledger.json - Release note: Tool-call header argument preview no longer splits a character:
truncateArg(tui/tool_execution.go) cut tomax-1bytes, so a non-ASCII argument longer than the budget ended in a half character that the JSON quoting turned into a visible U+FFFD before the ellipsis (text:"ééé…é�…"); it now backs up to the start of a character. RegressionTestFormatToolArgsTruncatesAtACharacterBoundary(red before: é, 日 and 🙂 at four byte offsets; both mutations, no back-up and cutting atmax, red);./tui -racegreen (1,139 s). Found by scanning production code for bytelen()comparisons against limits.HeaderForTool's fallback (FormatToolArgs:name path:"x") is PiG's own format for a tool with no builtin formatter and no definition, where Pi rendersformatToolCallWithArgs; that mismatch is the open "tool card with no definition" ruling request in questions/lg-help-4.md.
BF-8070fcc
OpenAI Codex and Azure OpenAI requests parse their URL as fetch does again: an upper-case scheme, a space or…
- Source: PiG's own defect
- Symptom: OpenAI Codex and Azure OpenAI requests parse their URL as fetch does again: an upper-case scheme, a space or a dot segment in a configured base URL reaches the server as the serialized URL, and the fragment is not sent.
- Root cause: An earlier change routed Codex and Azure through newFetchRequest, which hands its input to nodeurl.RequestURL as an already-serialized href. Codex's resolveCodexUrl text is not serialized, so a base URL like HTTP://host went out with scheme HTTPS-cased ('fetch failed'), a space went out raw, and an unparsable path sent its fragment. undici's Request parses the input first: newFetchRequest now does (TypeError 'Failed to parse URL from'), drops the fragment for the global fetch, and refuses credentials with fetch's TypeError. The openai SDK callers (completions, responses incl. Azure, openrouter-images) report any of these as 'Connection error.'; Codex, which calls fetch itself…
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8070fcc38b(2026-10-10) - Tests:
ai/fetch_request_url_test.go::TestCodexAndAzureRequestTargetIsParsedLikeFetch,ai/fetch_request_url_test.go::TestCodexRequestTargetDropsTheFragmentOfAnOpaquePath - Release note: OpenAI Codex and Azure OpenAI requests parse their URL as fetch does again: an upper-case scheme, a space or a dot segment in a configured base URL reaches the server as the serialized URL, and the fragment is not sent. Codex reports a base URL with credentials with fetch's own message.
BF-8085dc0
A legacy oauth.json or settings.json that starts with a byte order mark migrates to auth.json, as in Pi
- Source: PiG's own defect
- Symptom: A legacy
oauth.jsonorsettings.jsonthat starts with a byte order mark migrates toauth.json, as in Pi. - Root cause: migrateAuthToAuthJson parses JSON.parse(stripBom(...)) for both files (migrations.ts:36,51). Pig parsed the raw bytes, so a BOM-prefixed file failed to parse and its credentials were not migrated. Regression: TestMigrateAuthToAuthJSONStripsTheBOM (red before).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2e003bf45b(2026-10-06),410d7332a8(2026-10-06),8085dc062b(2026-10-06),8d23cfcee3(2026-10-06),fbd3640da9(2026-10-06) - Tests:
ai/auth_credential_payload_test.go::TestCredentialCustomJSONRetainsUTF16Payload,ai/auth_stringify_test.go::TestAuthStorageWritesJSONStringifyText,internal/codingagent/migrations_auth_order_test.go::TestMigrateAuthToAuthJSONStripsTheBOM,internal/codingagent/migrations_auth_order_test.go::TestMigrateAuthToAuthJSONWritesFilesInPiKeyOrder,internal/codingagent/settings_key_order_test.go::TestSaveSettingsPatchKeepsNestedKeyOrder,internal/codingagent/settings_key_order_test.go::TestSaveSettingsPatchKeepsTheFileKeyOrder,internal/codingagent/settings_key_order_test.go::TestSaveSettingsPatchReplacesInPlace,internal/codingagent/settings_key_order_test.go::TestSaveSettingsPatchWritesJSONStringifyTextand 1 more in docs/bug-ledger.json - Release note: A legacy
oauth.jsonorsettings.jsonthat starts with a byte order mark migrates toauth.json, as in Pi.
BF-80970da
The Go SDK page now says that leaving AgentDir empty in CreateAgentSessionServicesOptions makes a program…
- Source: PiG's own defect
- Symptom: The Go SDK page now says that leaving
AgentDirempty inCreateAgentSessionServicesOptionsmakes a program readPIG_CODING_AGENT_DIRthe way thepigcommand does. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
80970da292(2026-10-11) - Tests: None recorded.
- Release note: The Go SDK page now says that leaving
AgentDirempty inCreateAgentSessionServicesOptionsmakes a program readPIG_CODING_AGENT_DIRthe way thepigcommand does.
BF-80b912a
A failed install-receipt write during pig update or install, such as one rejected because the update CA…
- Source: PiG's own defect
- Symptom: A failed install-receipt write during
pig updateor install, such as one rejected because the update CA file is not owner-only, no longer keeps its staged.install-receipt-*temp file open. - Root cause: A failed install-receipt write during
pig updateor install, such as one rejected because the update CA file is not owner-only, no longer keeps its staged.install-receipt-*temp file open. - User impact: Credential or permission exposure
- Platform: Windows
- Fixed in: Unreleased
- Fix commits:
80b912a3e2(2026-10-24) - Tests: None recorded.
- Release note: A failed install-receipt write during
pig updateor install, such as one rejected because the update CA file is not owner-only, no longer keeps its staged.install-receipt-*temp file open. The handle is closed before the temp file is removed, so no stray file is left on Windows.
BF-80bb522
Compacting a history longer than the model's context window no longer fails with the provider's "prompt is…
- Source: PiG's own defect
- Symptom: Compacting a history longer than the model's context window no longer fails with the provider's "prompt is too long" error: the history is summarized in window-sized chunks, each chunk updating the summary of the one before, a split turn's over-window prefix joins those chunks, and a chunk the provider still refuses is halved (Pi 1.2.0 sends the whole history in one request and fails; D167).
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
80bb5229b5(2026-10-12) - Tests: None recorded.
- Release note: Compacting a history longer than the model's context window no longer fails with the provider's "prompt is too long" error: the history is summarized in window-sized chunks, each chunk updating the summary of the one before, a split turn's over-window prefix joins those chunks, and a chunk the provider still refuses is halved (Pi 1.2.0 sends the whole history in one request and fails; D167).
BF-80bb522-2
Gemini 2.x models (Google Generative AI and Vertex) no longer reject a conversation in which a parallel tool…
- Source: A Pi defect that PiG fixed first (divergence D115)
- Symptom: Gemini 2.x models (Google Generative AI and Vertex) no longer reject a conversation in which a parallel tool call other than the last returned an image.
- Root cause: Gemini answers the function calls of one model turn only when the user turn directly after it holds a function response for every call ("Please ensure that the number of function response parts is equal to the number of function call parts of the function call turn", Google function-calling documentation; the same file's comment: "Cloud Code Assist API requires all function responses to be in a single user turn"). A parallel tool batch with an image in any result but the last gets HTTP 400 on every later turn of the session.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: none recorded
- Removed when Pi fixes it: Pi keeps the function responses of one model turn in one user turn (Pi's routing test changes), at which point delete the deferral and the two rewrites.
- Commit that wrote the release note (the fix commit is not identified):
82a06d586d(2026-10-09),85152a825d(2026-10-10),80bb5229b5(2026-10-12) - Tests:
ai/google_contract_0861_test.go::TestGeminiToolResultImageOrderingMatchesUpstream,ai/google_generative_ai_payload_oracle_test.go::TestGoogleGenerativeAIPayloadMatchesPi,ai/google_generative_ai_payload_oracle_test.go::TestGoogleVertexPayloadMatchesPi,ai/google_image_tool_results_d115_test.go::TestGoogleImageToolResultOfASingleCallMatchesPiD115,ai/google_image_tool_results_d115_test.go::TestGoogleImageToolResultsKeepFunctionResponsesInOneTurnD115,test/pipeline-diff/diff_test.go::TestAIPipelineMatchesPi,test/pipeline-diff/diff_test.go::TestPipelineMatchesPi - Release note: Gemini 2.x models (Google Generative AI and Vertex) no longer reject a conversation in which a parallel tool call other than the last returned an image. PiG keeps all function responses of one model turn in a single user turn and sends the image turns after them. Pi 1.2.0 splits that turn (D115).
BF-80bb522-3
Amazon Bedrock Converse requests merge adjacent messages that have the same role
- Source: A Pi defect that PiG fixed first (divergence D116)
- Symptom: Amazon Bedrock Converse requests merge adjacent messages that have the same role.
- Root cause: the Converse API requires the roles of a conversation to alternate and rejects two consecutive messages of one role with a ValidationException (HTTP 400). The shape is common: aborting a turn while tools run and then sending a new prompt gives
assistant(toolUse), user(toolResult...), user(prompt), and every later turn of the session then fails. - User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: none recorded
- Removed when Pi fixes it: Pi merges or otherwise alternates adjacent same-role messages in its Converse converter.
- Commit that wrote the release note (the fix commit is not identified):
a00c6eda58(2026-10-10),ee4d7a20b0(2026-10-10),80bb5229b5(2026-10-12) - Tests:
ai/bedrock_alternation_d116_test.go::TestBedrockMergesAdjacentSameRoleMessagesD116,ai/bedrock_convert_upstream_test.go::TestBedrockUpstreamConvertMessages,ai/bedrock_payload_oracle_test.go::TestBedrockConversePayloadMatchesPi,ai/bedrock_test.go::TestBedrockConvertMessages_SkipsEmptyAssistant - Release note: Amazon Bedrock Converse requests merge adjacent messages that have the same role. A prompt sent after an abort during tool execution, or after a dropped assistant turn, no longer makes every later turn fail with a role-alternation error. Pi 1.2.0 sends two consecutive user messages (D116).
BF-80c797d
Text that holds ill-formed UTF-8 is written with one U+FFFD for each ill-formed sequence, as Node decodes it…
- Source: PiG's own defect
- Symptom: Text that holds ill-formed UTF-8 is written with one U+FFFD for each ill-formed sequence, as Node decodes it, instead of one per byte (Durable core records and the new
internal/jsparitywriters). - Root cause: Lead ruling 2026-10-11T00:27Z: the caller's insertion order of requestMetadata reaches the wire (parity, no D-number). Pi passes options.requestMetadata to the AWS SDK, which writes the object in its own-key order (bedrock-converse-stream.ts; ConverseStreamRequest$).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
80c797da93(2026-10-10),afa1dd5185(2026-10-10) - Tests:
ai/bedrock_request_body_oracle_test.go::TestBedrockRequestBodyMatchesPi,ai/bedrock_wire_metadata_order_test.go::TestBedrockStringMapEnumeratesLikeAJavaScriptObject,ai/google_request_body_oracle_test.go::TestGoogleRequestBodyMatchesPi,coding/extension/host/subprocess/native_stream_options_union_test.go::TestNativeProviderOptionsKeepRawValuesAndNumericPresence,coding/extension/host/subprocess/request_metadata_order_test.go::TestCompleteRequestKeepsRequestMetadataOrder,coding/extension/host/subprocess/request_metadata_order_test.go::TestModelStreamRequestKeepsRequestMetadataOrder,coding/extension/host/subprocess/request_metadata_order_test.go::TestProviderOptionsWireKeepsRequestMetadataOrder,internal/codingagent/native_thinking_options_test.go::TestSubprocessRequestMetadataKeepsKeyOrderand 4 more in docs/bug-ledger.json - Release note: Text that holds ill-formed UTF-8 is written with one U+FFFD for each ill-formed sequence, as Node decodes it, instead of one per byte (Durable core records and the new
internal/jsparitywriters).
BF-81e584b
anthropic-messages payload now matches Pi for four cases found by the new live-Pi oracle…
- Source: PiG's own defect
- Symptom: anthropic-messages payload now matches Pi for four cases found by the new live-Pi oracle
TestAnthropicMessagesCompatAndOptionsPayloadMatchesPi(ai/anthropic.go, anthropic-messages.ts:1132, 949-990, 1154-1164; 1,500 probes oversupportsMidConvoEffort/supportsMidConvoSystemMessages/supportsMidConvoToolChanges/allowedFallbackModels/forceAdaptiveThinking,interleavedThinking,thinkingBudgets, env cache retention, object tool choices, odd metadata, modelmaxTokens,thinkingLevelMap, and mid-conversation system messages with sections and tool changes): (1) the initial system message is the transcript's first message as given, so a mid-conversation system message after a dropped errored/aborted assistant turn is no longer taken for the prompt; (2) a managed-effort request withoutforceAdaptiveThinkingtakes the efforthigh(streamSimple derives it only for adaptive models); (3) an unmappedxhigh/maxlevel ishigh(mapThinkingLevelToEffort has no such case); (4) a managed-effort simple request still gets the budget-adjustedmax_tokens. openai-codex-responses now picks the instruction role of a mid-conversation system message from the selected model'sreasoningflag instead of alwaysdeveloper; the bedrock, google and responses payload oracles now draw mid-conversation system messages too. - Root cause: Real fixes: anthropic.go buildParams initial system message from the untransformed transcript; managed effort 'high' unless forceAdaptiveThinking; unmapped xhigh/max -> high; managed simple max_tokens adjustment. openai_responses.go: codex instruction role from the selected model's reasoning flag.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),81e584bdba(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/anthropic_effort_strict_test.go::TestAnthropicManagedEffortMapping,ai/anthropic_messages_compat_options_oracle_test.go::TestAnthropicMessagesCompatAndOptionsPayloadMatchesPi,ai/bedrock_payload_oracle_test.go::TestBedrockConversePayloadMatchesPi,ai/openai_completions_compat_options_oracle_test.go::TestOpenAICompletionsCompatAndCacheOptionsPayloadMatchesPiand 4 more in docs/bug-ledger.json - Release note: anthropic-messages payload now matches Pi for four cases found by the new live-Pi oracle
TestAnthropicMessagesCompatAndOptionsPayloadMatchesPi(ai/anthropic.go, anthropic-messages.ts:1132, 949-990, 1154-1164; 1,500 probes oversupportsMidConvoEffort/supportsMidConvoSystemMessages/supportsMidConvoToolChanges/allowedFallbackModels/forceAdaptiveThinking,interleavedThinking,thinkingBudgets, env cache retention, object tool choices, odd metadata, modelmaxTokens,thinkingLevelMap, and mid-conversation system messages with sections and tool changes): (1) the initial system message is the transcript's first message as given, so a mid-conversation system message after a dropped errored/aborted assistant turn is no longer taken for the prompt; (2) a managed-effort request withoutforceAdaptiveThinkingtakes the efforthigh(streamSimple derives it only for adaptive models); (3) an unmappedxhigh/maxlevel ishigh(mapThinkingLevelToEffort has no such case); (4) a managed-effort simple request still gets the budget-adjustedmax_tokens. openai-codex-responses now picks the instruction role of a mid-conversation system message from the selected model'sreasoningflag instead of alwaysdeveloper; the bedrock, google and responses payload oracles now draw mid-conversation system messages too. Mutation-checked: 10 mutants red (initial from transformed list, effort default, managed max_tokens, xhigh case, fallbacks model/beta, interleaved beta, mid-convo effort beta, native tool changes anchor).
BF-81e584b-2
Live-Pi oracle TestMistralMessagesPayloadMatchesPi (ai/mistral_messages_payload_oracle_test.go…
- Source: PiG's own defect
- Symptom: Live-Pi oracle
TestMistralMessagesPayloadMatchesPi(ai/mistral_messages_payload_oracle_test.go, mistral-conversations.ts toChatMessages and options; 1,200 random transcripts with thinking, errored/aborted turns, foreign tool calls, tool results with images and mid-conversation system messages, thinking level maps, sampling options, tool choices): the earlier Mistral payload oracles drew one user message, so the message conversion was only checked by two hand-written bodies. - Root cause: Real fixes: anthropic.go buildParams initial system message from the untransformed transcript; managed effort 'high' unless forceAdaptiveThinking; unmapped xhigh/max -> high; managed simple max_tokens adjustment. openai_responses.go: codex instruction role from the selected model's reasoning flag.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),81e584bdba(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/anthropic_effort_strict_test.go::TestAnthropicManagedEffortMapping,ai/anthropic_messages_compat_options_oracle_test.go::TestAnthropicMessagesCompatAndOptionsPayloadMatchesPi,ai/bedrock_payload_oracle_test.go::TestBedrockConversePayloadMatchesPi,ai/mistral_messages_payload_oracle_test.go::TestMistralMessagesPayloadMatchesPiand 5 more in docs/bug-ledger.json - Release note: Live-Pi oracle
TestMistralMessagesPayloadMatchesPi(ai/mistral_messages_payload_oracle_test.go, mistral-conversations.ts toChatMessages and options; 1,200 random transcripts with thinking, errored/aborted turns, foreign tool calls, tool results with images and mid-conversation system messages, thinking level maps, sampling options, tool choices): the earlier Mistral payload oracles drew one user message, so the message conversion was only checked by two hand-written bodies. No payload difference. Mutation-checked: dropping the initial-system slice, the tool-result error text and the image data-URL prefix go red;sanitizeSurrogateson the system prompt is not drawn (generated text has no lone surrogate).
BF-8241433
A Mistral stream chunk with a member of an unexpected type is read as Pi's JavaScript reads it instead of…
- Source: PiG's own defect
- Symptom: A Mistral stream chunk with a member of an unexpected type is read as Pi's JavaScript reads it instead of ending the turn with a JSON decode error: a finish reason that is not a string ends the turn with
Provider stopped with: <its string form>, a content item's non-string text ends it with Pi'stext.replace is not a function, a content item that is not an object is skipped, atool_callsvalue that is not an array ends it withtoolCalls is not iterable(a string with Pi's error for its first character), a usage that is not an object reads as zeros, a prompt count sent as a string counts as its number, and a cached-token count that is not a number reads as 0. - Root cause: processResponsesStream reads each member with its own expression (openai-responses-shared.ts:463-800). PiG ended the turn with "openai-responses: invalid SSE JSON" for a mistyped event member and silently skipped an output_item.added/.done whose item did not decode (the two AI-08 stream-parse-continue hits), where Pi: - appends String(delta) with
+=on every delta event, "null"/"undefined" for a null/absent delta; - joins a done message'scontent.map((c) => c.type === "output_text" ? c.text : c.refusal).join("")and a done reasoning item's summary/content texts with String() for each value and "" for null/undefined, and throwsitem.content?.map is not a function(or summary) for a… - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5078f73100(2026-10-11),5932542c21(2026-10-11),71e9d13eb9(2026-10-11),82414338e7(2026-10-11),8eb1202528(2026-10-11),b6afa7be1a(2026-10-11) - Tests:
ai/anthropic_event_member_type_oracle_test.go::TestAnthropicEventMistypedMembersMatchPi,ai/google_chunk_member_type_oracle_test.go::TestGoogleChunkMistypedMembersMatchPi,ai/mistral_chunk_member_type_oracle_test.go::TestMistralChunkMistypedMembersMatchPi,ai/openai_responses_member_type_oracle_test.go::TestOpenAIResponsesMistypedMembersMatchPi - Release note: A Mistral stream chunk with a member of an unexpected type is read as Pi's JavaScript reads it instead of ending the turn with a JSON decode error: a finish reason that is not a string ends the turn with
Provider stopped with: <its string form>, a content item's non-string text ends it with Pi'stext.replace is not a function, a content item that is not an object is skipped, atool_callsvalue that is not an array ends it withtoolCalls is not iterable(a string with Pi's error for its first character), a usage that is not an object reads as zeros, a prompt count sent as a string counts as its number, and a cached-token count that is not a number reads as 0.
BF-82d1808
Subprocess extensions no longer slow down each streamed token in long sessions: the state sent before each…
- Source: PiG's own defect
- Symptom: Subprocess extensions no longer slow down each streamed token in long sessions: the state sent before each event reuses the context usage estimate and the session name until the session changes.
- Root cause: The host pushes a state snapshot to a subprocess extension before every event it dispatches (subprocess host.go pushStateTo), and the snapshot carries getContextUsage and getSessionName. Both walked the whole log: ContextUsage projected the branch and getSessionName scanned every entry backwards, so each event, including one message_update per streamed delta, cost O(history): 2 MB and 4.3 ms per event at 500 exchanges. Pi computes context usage only when an extension calls ctx.getContextUsage() (agent-session.ts getContextUsage).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
10aebd5c05(2026-10-09),82d1808c64(2026-10-10) - Tests:
ai/complexity_scaling_norace_test.go::TestScalingProviderRequestPerTurn,ai/complexity_scaling_norace_test.go::TestScalingProviderStreamPerDelta,coding/cli/complexity_scaling_norace_test.go::TestScalingExtensionEventStatePush,coding/complexity_scaling_norace_test.go::TestScalingSessionTurn,coding/extension/host/subprocess/complexity_scaling_norace_test.go::TestScalingMessageUpdatePayload,coding/session_context_usage_reuse_test.go::TestContextUsageReusesTheEstimateUntilTheLogOrWindowChanges,internal/codingagent/compaction/complexity_scaling_norace_test.go::TestScalingCompactionAndContextEstimate,internal/codingagent/complexity_scaling_norace_test.go::TestScalingSessionLogOperationsand 7 more in docs/bug-ledger.json - Release note: Subprocess extensions no longer slow down each streamed token in long sessions: the state sent before each event reuses the context usage estimate and the session name until the session changes.
BF-83d5488
pig install <dir> of a local directory with no package manifest and no extensions/, skills/, prompts/…
- Source: PiG's own defect
- Symptom: Previously Pig recorded the Package and loaded nothing from it.
- Root cause: Pi resolves every local source, configured Package or -e, through resolveLocalExtensionSource (package-manager.ts:1296-1299, 1377-1384): when collectPackageResources reports no filter, no "pi" manifest and no conventional resource directory (:2214-2262), the directory itself is one enabled extension.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0f6fdc2093(2026-10-07),382156cdb7(2026-10-07),66bb7180b3(2026-10-07),8723a419d7(2026-10-07),a79de03e7f(2026-10-07),7489735c3f(2026-10-09),83d54886a6(2026-10-09) - Tests:
coding/cli/package_local_directory_extension_test.go::TestInstalledLocalDirectoryWithoutPackageResourcesLoadsAsAnExtension,coding/cli/package_update_workers_test.go::TestPackageUpdateChecksBoundWorkers,coding/extension/host/subprocess/builder_node_entry_test.go::TestResolveNodeEntrypointImportsAnIndexMjsDirectoryAsJiti,coding/resource_loader_extend_test.go::TestResourceLoaderInterfaceExtendsAndReloads,internal/codingagent/interactive_test.go::TestHasAssistantUsageAfterCompaction,internal/codingagent/tools/bash_spawn_hook_test.go::TestBashSpawnHookTypeIsSharedByPowerShell,internal/packagemanager/local_file_source_test.go::TestLocalDirectoryPackageSourceThatDescribesResourcesIsNotItselfAnExtension,internal/packagemanager/local_file_source_test.go::TestLocalDirectoryPackageSourceWithoutResourcesIsItselfAnExtensionand 4 more in docs/bug-ledger.json - Release note:
pig install <dir>of a local directory with no package manifest and noextensions/,skills/,prompts/orthemes/directory now loads the directory itself as an extension, aspi installdoes. Previously Pig recorded the Package and loaded nothing from it.
BF-83fea11
On Windows, a refused connection reads as Node's connect ECONNREFUSED and is not retried, as in Pi; it was…
- Source: PiG's own defect
- Symptom: On Windows, a refused connection reads as Node's
connect ECONNREFUSEDand is not retried, as in Pi; it was reported as a retryablefetch failed. - Root cause: nodeNetErrorMessage accepted only the connect syscall, but Go names the Windows call connectex, so on Windows a refused connect fell through to 'fetch failed' and a retryable Bedrock error where Node reports 'connect ECONNREFUSED 127.0.0.1:N', not retryable (TestNodeNetErrorMessageIsNodesText, TestBedrockRefusedConnectIsNodesErrorLikePi on the release PR's Windows native-2 job).
- User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
83fea11dbf(2026-10-15) - Tests:
ai/bedrock_error_metadata_upstream_test.go::TestBedrockRefusedConnectIsNodesErrorLikePi,ai/node_net_error_test.go::TestNodeNetErrorMessageIsNodesText,ai/oauth_page_corpus_test.go::TestOAuthPageCorpusMatchesThePinnedPackage - Release note: On Windows, a refused connection reads as Node's
connect ECONNREFUSEDand is not retried, as in Pi; it was reported as a retryablefetch failed.
BF-844d215
Repairing a partial tool-call arguments text follows partial-json's obj["__proto__"] = value: a…
- Source: PiG's own defect
- Symptom: Repairing a partial tool-call arguments text follows partial-json's
obj["__proto__"] = value: a"__proto__": nullmember leaves the object without a prototype, so a later"__proto__"member is an ordinary member, as in Pi (it was dropped). - Root cause: partial-json builds objects with obj[key] = value. A "proto" member runs Object.prototype's setter: null makes the prototype null, an object or array replaces it, any other value is ignored, and none creates a property. Once the prototype is null there is no setter, so a later "proto" member is an ordinary member. parseMembers dropped every "proto" member.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
844d2155d8(2026-10-11) - Tests:
ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,ai/streaming_json_resume_test.go::TestStreamingArgumentsParserMatchesWholeTextParser - Release note: Repairing a partial tool-call arguments text follows partial-json's
obj["__proto__"] = value: a"__proto__": nullmember leaves the object without a prototype, so a later"__proto__"member is an ordinary member, as in Pi (it was dropped).
BF-848836a
fix(frontmatter): header starts one UTF-16 unit after the fence
- Source: PiG's own defect
- Symptom: fix(frontmatter): header starts one UTF-16 unit after the fence
- Root cause: Pi slices the YAML header with normalized.slice(4, endIndex); Go cut at byte 4, which splits a multi-byte character after '---'. Adds a random-document differential (6000 documents plus nine fixed rows, 8+ red before).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
848836a581(2026-10-10) - Tests:
internal/codingagent/frontmatter/pi_random_oracle_test.go::TestParseRandomDocumentsMatchesPi
BF-852652e
PiG no longer names documentation that does not exist
- Source: PiG's own defect
- Symptom: They named
<config root>/docsfiles that were never written. - Root cause: Pi names the docs of its installed package, which exist wherever Pi runs. PiG writes its bundle under the config root at startup (D22) and ignored a failed write, so a config root this user cannot write and that held no bundle of the running build left the system prompt, login help and the codemode description naming files that were never written.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
852652e3ae(2026-10-09),da99854d1b(2026-10-09) - Tests:
coding/cli/piglet_verify_cache_startup_test.go::TestSignedPigletBinaryRunsWhenItsVerifyCacheCannotBeWritten,coding/cli/readonly_config_root_binary_test.go::TestReadOnlyConfigRootStartsQuietlyAndNamesOnlyDocsThatExist,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,internal/codingagent/auth_guidance_test.go::TestNoTextNamesADocsBundleStartupCouldNotWrite - Release note: PiG no longer names documentation that does not exist. When the config root (
PIG_HOME) cannot be written and holds no documentation bundle of the running build, the system prompt has no docs section, and login help and the codemode description name no docs page. Before, they named<config root>/docsfiles that were never written.pig docs showstill prints the embedded pages.
BF-852f029
Durable document, conversation-view and task-graph states report failing state listeners to the Session's…
- Source: PiG's own defect
- Symptom: Durable document, conversation-view and task-graph states report failing state listeners to the Session's report, as Pi does, instead of dropping them.
- Root cause: Ports packages/chord/src/state.ts at Pi (moved from services/state.ts) into the public chord package: - NewReplicatedState is replicatedState(initial) (state.ts:404-419, MutableReplicatedStateImpl :240-290): Change and ChangeArray draft through delta.Change, Replace adopts an alias-free root, a deeply equal replacement publishes nothing, a change or replace from a change callback is rejected (state.ts:256, :281). - The mutable state has no exact-op subscription any more, so a listener failure never reaches Change or Replace: StateSubscriber reports it through reportErrorAsync (state.ts:146-169, :394-398), the Go uncaught reporter, after the revision is committed, and delivery to later…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
852f029c26(2026-10-12) - Tests:
chord/json_test.go::TestIsJsonValueChecksStrictJSONWithoutNormalizing,chord/replicated_state_delivery_upstream_test.go::TestAttachedReplicatedStateListenerErrors,chord/replicated_state_delivery_upstream_test.go::TestMutableReplicatedStateReportsRejectedCallbacksThroughItsDefaultReporter,chord/replicated_state_delivery_upstream_test.go::TestReplicatedStatePublicDelivery,chord/replicated_state_upstream_test.go::TestAuthoritativeReplicatedStateSources,chord/replicated_state_upstream_test.go::TestTransactionalReplicatedState,durable/session/session_watches_test.go::TestSessionDocumentWatches,internal/goroutineid/goroutineid_test.go::TestCurrentIdentifiesTheCallingGoroutine - Release note: Durable document, conversation-view and task-graph states report failing state listeners to the Session's report, as Pi does, instead of dropping them.
BF-85618d9
fix(rpc): a failed switch_session answers before the replacement ahead of it; prompt compaction checks…
- Source: PiG's own defect
- Symptom: fix(rpc): a failed switch_session answers before the replacement ahead of it; prompt compaction checks resolve in order
- Root cause: (2) The prompt compaction check shares one helper between CheckPromptCompaction and BeginPromptCompactionCheck; the overflow guard reset and the replayed overflow response are pinned. (3) A check that follows a compaction still running decides behind it off the loop, so the prompts of one batch resolve in order (the Session lock race made p2 answer first in 1 of 3 runs); pinned by TestRPCPromptsWithDueCompactionsAnswerInRequestOrder (red when the chain is cut). rpcResolvedChannel no longer steals rpcAwaitInOrder's doc comment.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
85618d9a25(2026-10-10) - Tests:
coding/cli/rpc_prompt_order_test.go::TestRPCPromptsWithDueCompactionsAnswerInRequestOrder,coding/cli/rpc_replacement_order_test.go::TestRPCEarlyReplacementErrorFollowsTheCompactionStart,coding/cli/rpc_replacement_order_test.go::TestRPCFailedSwitchSessionAnswersBeforeTheReplacementAheadOfIt,coding/session_prompt_compaction_check_test.go::TestBeginPromptCompactionCheckReturnsOnlyADueCompaction,coding/session_prompt_compaction_check_test.go::TestPromptCompactionCheckReplaysAnOverflowedResponse
BF-85908ae
A /tree branch summary the provider refuses as too long is retried with the newest half of the request…
- Source: A Pi defect that PiG fixed first (divergence D167)
- Symptom: A
/treebranch summary the provider refuses as too long is retried with the newest half of the request, never less than the newest message, until it is accepted (D167). - Root cause: D167.md, the pigdocs row, the changelog and the CompactUsing marker now state it, and the evidence lists its tests. The pigdocs D167 row moves from 'Additive features' to 'Behavior differences': D167 changes a Pi behaviour.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: none recorded
- Removed when Pi fixes it: Pi summarizes over-window histories itself (chunking or trimming the summary input).
- Fix commits:
78e2b7c5fc(2026-10-11),85908ae8a9(2026-10-11),c8be31c504(2026-10-11),f412b3de4e(2026-10-11),54b89d33cd(2026-10-13) - Tests:
internal/codingagent/compaction/generate_summary_oracle_test.go::TestGenerateSummaryMatchesPi,internal/codingagent/compaction/over_window_history_test.go::TestGenerateBranchSummaryHalvesARequestTheProviderRefusesAsTooLong,internal/codingagent/compaction/over_window_history_test.go::TestGenerateBranchSummaryOfABranchThatFitsIsOneRequest,internal/codingagent/compaction/over_window_history_test.go::TestGenerateBranchSummaryOfOneRefusedMessageKeepsTheProvidersError,internal/codingagent/compaction/over_window_history_test.go::TestGenerateSummaryHalvesAChunkTheProviderRefusesAsTooLong,internal/codingagent/compaction/over_window_history_test.go::TestGenerateSummaryOfAHistoryLongerThanTheWindowIsChunked,internal/codingagent/compaction/over_window_history_test.go::TestGenerateSummaryOfAHistoryThatFitsTheWindowIsOneRequest,internal/codingagent/compaction/over_window_history_test.go::TestGenerateSummaryOfOneMessageLongerThanTheWindowKeepsTheProvidersErrorand 6 more in docs/bug-ledger.json - Release note: A
/treebranch summary the provider refuses as too long is retried with the newest half of the request, never less than the newest message, until it is accepted (D167).
BF-85a7b01
A prompt now fails with Pi's error when the Session's history holds a system message whose content is a…
- Source: PiG's own defect
- Symptom: PiG read such a member as absent and sent the request.
- Root cause: AgentSession.prompt runs _preparePromptAndToolLoadout after before_agent_start; it applies the run's tool loadout and then replays the transcript's system messages with getCurrentSystemMessage (agent-session.ts:1738-1748). The replay renders each system message's content with contentText and then applies every toolsRemoved and toolsAdded list (pi-ai utils/transcript.ts getCurrentSystemMessage, getCurrentTools; utils/text.ts contentText). Pi parses a session without validation, so a system message with a numeric content makes the prompt fail with "content.filter is not a function", and one with toolsAdded 5 with "number 5 is not iterable (cannot read property Symbol(Symbol.iterator))"; the…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),85a7b010a8(2026-10-10) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,agent/system_replay_error_test.go::TestCurrentSystemMessageErrorMatchesPi,internal/codingagent/interactive_prompt_validation_test.go::TestInteractivePromptValidationPrecedesBeforeAgentStart,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgainand 3 more in docs/bug-ledger.json - Release note: A prompt now fails with Pi's error when the Session's history holds a system message whose
contentis a number, a boolean or an object (content.filter is not a function), whose content array holdsnull, or whosetoolsAddedortoolsRemovedis a number, a boolean, an object or an array that holdsnull(for examplenumber 5 is not iterable (cannot read property Symbol(Symbol.iterator))). Before, PiG read such a member as absent and sent the request. In RPC mode the prompt response reports the error, and in interactive mode the error is shown; no run starts.
BF-85c98be
durable/interop/bench.mjs and sqlhost-bench seed|sample report the process's own peak RSS on Linux…
- Source: PiG's own defect
- Symptom:
durable/interop/bench.mjsandsqlhost-bench seed|samplereport the process's own peak RSS on Linux (VmHWM), asbench/durableperfalready did. getrusage's maxRSS lagged it by 6 to 20 MiB, and it took on the launcher's peak when that was larger: Go, Python and Node all spawn with vfork, so the child's maxRSS starts from the parent's. - Root cause: durable/interop/bench.mjs read process.resourceUsage().maxRSS and sqlhost-bench getrusage's ru_maxrss. On Linux that value carries the RSS high-water mark of the address space the process was exec'd from, and every launcher of these samples spawns with vfork: with a 512 MiB parent, a bench.mjs sample whose own VmHWM is 110.5-111.6 MiB reports 512.8-521.5 MiB from Go (exec.Command, also through taskset), Python (subprocess.run) and Node (spawnSync).
- User impact: Behavior differed from Pi
- Platform: Linux
- Fixed in: 0.5.0
- Fix commits:
85c98bed2b(2026-10-13) - Tests: None recorded.
- Release note:
durable/interop/bench.mjsandsqlhost-bench seed|samplereport the process's own peak RSS on Linux (VmHWM), asbench/durableperfalready did. getrusage's maxRSS lagged it by 6 to 20 MiB, and it took on the launcher's peak when that was larger: Go, Python and Node all spawn with vfork, so the child's maxRSS starts from the parent's.peakRssin the pi and pig-native lines of earlier Linux runs is low by that lag.
BF-85ca0f5
Rebuilding the transcript (resume, /tree, display settings) now places cache-miss notices as Pi does in a…
- Source: PiG's own defect
- Symptom: Rebuilding the transcript (resume,
/tree, display settings) now places cache-miss notices as Pi does in a session with branches: each response is compared with the request before it in the session file, as the live notice and/sessiondo. - Root cause: renderSessionItems calls collectCacheMisses(sessionManager.getEntries()), which scans the whole file in file order. PiG scanned only the rendered branch, so a response on a second branch missed the notice that the live path and /session report.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
85ca0f547b(2026-10-13) - Tests:
internal/codingagent/interactive_cache_miss_entries_test.go::TestRebuiltCacheMissNoticesScanEveryEntryInFileOrder - Release note: Rebuilding the transcript (resume,
/tree, display settings) now places cache-miss notices as Pi does in a session with branches: each response is compared with the request before it in the session file, as the live notice and/sessiondo. PiG compared only within the shown branch.
BF-860776a
A Google provider built from a model id without catalog metadata now sends its own earlier thinking blocks as…
- Source: PiG's own defect
- Symptom: The missing API made every earlier turn look like another model and the signature was dropped (found by the provider-wire parity scenario against the pinned Pi).
- Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),860776a9f5(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/google_same_model_signature_test.go::TestGoogleProviderKeepsItsOwnThinkingSignaturesWithoutCatalogMetadataand 3 more in docs/bug-ledger.json - Release note: A Google provider built from a model id without catalog metadata now sends its own earlier thinking blocks as thoughts with their signatures, as Pi does for the same provider, API and model. Before, the missing API made every earlier turn look like another model and the signature was dropped (found by the provider-wire parity scenario against the pinned Pi).
BF-86364b4
fix(minimatch): case-insensitive patterns compare as a JavaScript RegExp does
- Source: PiG's own defect
- Symptom: fix(minimatch): case-insensitive patterns compare as a JavaScript RegExp does
- Root cause: regexp2 lower-cases both sides, so a pattern letter or non-ASCII literal also matched characters that lower-case to it (İ to i, the Kelvin sign to k, the Angstrom sign to å) where JS /i compares toUpperCase and keeps a character whose upper case would become ASCII. ASCII letters now spell their two cases and non-ASCII literals and Canonicalize classes are case-sensitive groups.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
86364b4411(2026-10-10) - Tests:
internal/minimatch/minimatch_oracle_test.go::TestMatchAgainstNodeMinimatch
BF-866c7f9
fix(rpc): two prompts of one batch answer in request order
- Source: PiG's own defect
- Symptom: fix(rpc): two prompts of one batch answer in request order
- Root cause: Pi's check resumes without I/O unless it compacts, so the policy half now runs on the loop (Session.BeginPromptCompactionCheck) and only a due compaction leaves it, chained through rpcAwaitInOrder.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
866c7f9fb3(2026-10-10) - Tests:
coding/cli/rpc_prompt_order_test.go::TestRPCPromptsOfOneBatchAnswerInRequestOrder,coding/session_prompt_compaction_check_test.go::TestBeginPromptCompactionCheckReturnsOnlyADueCompaction
BF-869cef8
A pi-messages stream event that is a number, string, boolean or array is skipped, as Pi skips an event of…
- Source: PiG's own defect
- Symptom: A pi-messages stream event that is a number, string, boolean or array is skipped, as Pi skips an event of unknown type, instead of ending the reply with a JSON decode error.
- Root cause: It does not: the event decode into PiMessagesEvent failed with "json: cannot unmarshal number into Go value of type ai.PiMessagesEvent", and the stream ended in an error with no reply. Pi's readPiMessagesEvents drops only a falsy event (pi-messages.ts:290-292). createEventConverter returns
{ ...event, partial }for a truthy number, string, boolean or array, as for any event without a known type (:193-272), and the reply that follows streams. The agent loop ignores the typeless event (agent-loop.ts:415-445). - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
77790f284b(2026-10-11),869cef866a(2026-10-11),e720ec0d0a(2026-10-11) - Tests:
ai/bedrock_eventstream_integer_float_test.go::TestBedrockStreamAcceptsIntegerValuedFloats,ai/bedrock_eventstream_integer_float_test.go::TestBedrockStreamEventReadsIntegerValuedFloats,ai/openai_stream_oracle_test.go::TestOpenAICompletionsProviderStreamMatchesPi,ai/pi_messages_non_object_event_test.go::TestPiMessagesNonObjectEventIsSkippedLikeAnUnknownEvent,internal/jsjson/normalize_test.go::TestNormalizeIntegers - Release note: A pi-messages stream event that is a number, string, boolean or array is skipped, as Pi skips an event of unknown type, instead of ending the reply with a JSON decode error.
BF-86c411a
fix(builtin): codemode and tool_search declare their parameters in TypeBox key order
- Source: PiG's own defect
- Symptom: fix(builtin): codemode and tool_search declare their parameters in TypeBox key order
- Root cause: Probed through RPC get_messages with every built-in tool: all nine parameter objects are now byte-equal to Pi. No divergence (byte parity).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
86c411a481(2026-10-10) - Tests:
coding/extension/builtin/codemode/schema_order_test.go::TestInputSchemaKeepsTypeBoxKeyOrder,coding/extension/builtin/toolsearch/schema_order_test.go::TestInputSchemaKeepsTypeBoxKeyOrder
BF-871b48d
pig install <source> --validate-only now validates git and npm sources that are not installed yet
- Source: PiG's own defect
- Symptom:
pig install <source> --validate-onlynow validates git and npm sources that are not installed yet. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.3
- Fix commits:
871b48d401(2026-10-22) - Tests:
coding/cli/extension_validate_command_test.go::TestInstallValidateOnlyValidatesInstalledGitSource,coding/cli/extension_validate_command_test.go::TestInstallValidateOnlyValidatesUninstalledGitSourceInScratchDir,coding/cli/extension_validate_command_test.go::TestInstallValidateOnlyValidatesUninstalledNPMSourceInScratchDir - Release note:
pig install <source> --validate-onlynow validates git and npm sources that are not installed yet. It fetches them into a temporary directory that is removed afterwards, and no longer reportsstat …/git/<host>/<owner>/<repo>: no such file or directory. Installed packages and settings are not touched.
BF-87a6ac5
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: UnmarshalKeepingSurrogates is generic over its destination, so the call sites decode into their own concrete types. The perf-lint entry for FromJsonValue's typed decode keeps its count and follows the callee's new name. unmarshalOrdered is generic for the same reason.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
87a6ac512c(2026-10-25) - Tests: None recorded.
BF-88177e8
fix(cli): type the validate-only scratch-fetch marker as D28 and extend the record
- Source: PiG's own defect
- Symptom: fix(cli): type the validate-only scratch-fetch marker as D28 and extend the record
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
88177e8e3e(2026-10-22) - Tests: None recorded.
BF-8894a13
A double-quoted YAML string in a skill, prompt template or parseFrontmatter header now reads a…
- Source: PiG's own defect
- Symptom: Each half became U+FFFD.
- Root cause: A 6,000-message differential against Pi's own serializeConversation (node + dist) now has 0 differences in Go and Python. Before the fix it had 53 in Go and 33 in Python. The conformance row cannot see this, because the harness decodes reports with encoding/json, which turns every lone surrogate into U+FFFD.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
15cb4f2db8(2026-10-11),50f526d69e(2026-10-11),8894a13f34(2026-10-11),a178939d6a(2026-10-11),edf4304a1c(2026-10-11) - Tests:
extensions/sdk-py/tests/test_serialize_conversation_surrogates.py,extensions/sdk/serialize_conversation_surrogate_test.go::TestSerializeConversationKeepsUnpairedSurrogates,internal/codingagent/frontmatter/ordered_test.go::TestParseOrderedListsKeysInInsertionOrder,internal/yaml12/surrogate_escape_test.go::TestDoubleQuotedSurrogateEscapesJoinIntoOneCharacter,test/extension-conformance/host_parser_helpers_test.go::TestConformance_HostParserHelpersAgreeAcrossSDKs,test/extension-conformance/session_skill_helpers_test.go::TestConformance_SessionAndSkillHelpersAgreeAcrossSDKs - Release note: A double-quoted YAML string in a skill, prompt template or
parseFrontmatterheader now reads a surrogate-pair escape such as"\uD83D\uDE00"as the one character it encodes, as Pi does. Before, each half became U+FFFD.
BF-8894a13-2
The liveness conformance harness answers the host-parser helper calls a factory makes before the register…
- Source: PiG's own defect
- Symptom: The liveness conformance harness answers the host-parser helper calls a factory makes before the register frame, so the recording of the five SDK cells is unchanged.
- Root cause: The yaml library builds a double-quoted scalar from UTF-16 units (String.fromCodePoint per escape), so "\uD83D\uDE00" is U+1F600: Pi's parseFrontmatter reads a: "\uD83D\uDE00" as {"a":"😀"}. yaml12 turned each surrogate escape into U+FFFD on its own, so skills, prompt templates and the new frontmatter.parse host call gave two U+FFFD. doubleQuotedValue now holds a high-surrogate escape until the next thing the scalar receives and joins it with a low-surrogate escape; a surrogate left unpaired stays U+FFFD (a Go string cannot hold it).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
15cb4f2db8(2026-10-11),8894a13f34(2026-10-11),edf4304a1c(2026-10-11),f51d8b4bea(2026-10-11) - Tests:
internal/codingagent/frontmatter/ordered_test.go::TestParseOrderedListsKeysInInsertionOrder,internal/yaml12/surrogate_escape_test.go::TestDoubleQuotedSurrogateEscapesJoinIntoOneCharacter,test/extension-conformance/compaction_usage_test.go::TestConformance_CompactionUsageHelpersAgreeAcrossSDKs,test/extension-conformance/host_parser_helpers_test.go::TestConformance_HostParserHelpersAgreeAcrossSDKs - Release note: The liveness conformance harness answers the host-parser helper calls a factory makes before the register frame, so the recording of the five SDK cells is unchanged.
BF-893ff97
A read call whose offset or limit arrives as a string shows the right line range (:25-37, not…
- Source: PiG's own defect
- Symptom: A
readcall whoseoffsetorlimitarrives as a string shows the right line range (:25-37, not:25-2512) in the TUI and in exported HTML (Pi #9887). - Root cause: read.ts formatReadLineRange and export template.js add raw args with JS +; offset 25 / limit "13" rendered :25-2512.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
893ff9708b(2026-10-09) - Tests: None recorded.
- Release note: A
readcall whoseoffsetorlimitarrives as a string shows the right line range (:25-37, not:25-2512) in the TUI and in exported HTML (Pi #9887).
BF-89d167f
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: EACCES) and crashes when it must write auth.json or sessions; it has no SDK stage or Binary verify cache. PiG's two additions stay quiet on EROFS/EACCES/EPERM and still warn otherwise.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
89d167fe18(2026-10-09) - Tests:
coding/cli/readonly_config_root_test.go::TestReadOnlyConfigRootError
BF-8a18a6b
A Node extension no longer keeps reading an entry the host refused
- Source: PiG's own defect
- Symptom: A Node extension no longer keeps reading an entry the host refused.
- Root cause: Fixes #264
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
8a18a6bb2d(2026-10-21, #272) - Tests:
coding/extension/host/subprocess/runtime_node_refused_append_test.go::TestNodeExtensionReadsExactlyTheSessionTheHostHolds,coding/extension/host/subprocess/runtime_node_refused_append_test.go::TestNodeRuntimeStopsShowingAnAppendTheHostRefused,test/extension-conformance/session_refused_append_test.go::TestSessionMirrorHoldsOnlyWhatTheHostAppendedAcrossSDKs - Release note: A Node extension no longer keeps reading an entry the host refused. When the host answers
pi.appendEntry(orctx.sessionManager.appendCustomEntry) with an error,getEntries(),getBranch(),getLeafId(),getEntry()and the other session reads stop showing that entry, also after later calls the host synchronizes; before, the entry stayed in the extension's session reads although the host's log never held it. Only the refused entry is taken back: appends still in flight stay, and an entry the host appended is read with the parent the host linked it to, where the extension could show the refused entry or an older leaf instead. A call that was cancelled or lost with its connection has no answer, so its entry stays until the host's log settles it.
BF-8a25539
llama-cpp-classify answers match Pi bit for bit: the softmax uses jsmath.Exp, V8's fdlibm Math.exp…
- Source: PiG's own defect
- Symptom:
llama-cpp-classifyanswers match Pi bit for bit: the softmax usesjsmath.Exp, V8's fdlibmMath.exp(Go'smath.Expdiffers in the last bit for some arguments on amd64). - Root cause: request-body-lone-surrogate, completions-empty-sanitized-text, llama-cpp-classify-pi-parity, openai-decisions-input-order and openrouter-images-edge-responses get the sha of the commit that added them (TestChangelogFragmentsFollowTheRules). llama-cpp-classify-pi-parity: 'so the server receives what it receives from Pi' for the forbidden phrase 'same requests' (TestPublicClaimsMatchTheEvidence).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
8a25539ce3(2026-10-13) - Tests:
test/docs-drift/changelog_fragments_test.go::TestChangelogFragmentsFollowTheRules,test/docs-drift/public_claims_test.go::TestPublicClaimsMatchTheEvidence - Release note:
llama-cpp-classifyanswers match Pi bit for bit: the softmax usesjsmath.Exp, V8's fdlibmMath.exp(Go'smath.Expdiffers in the last bit for some arguments on amd64). Its requests are written with their members in Pi's order, and a failed lookup no longer cancels the sibling requests of the samePromise.all, so the server receives what it receives from Pi.
BF-8a88c34
On Windows, a custom theme edited in a rapid burst of writes no longer stays on a stale version
- Source: PiG's own defect
- Symptom: On Windows, a custom theme edited in a rapid burst of writes no longer stays on a stale version.
- Root cause: An overflow of the native change buffer loses events but leaves the watch armed. The run loop closed the native watcher on every error, so a burst of writes that overflowed the Windows ReadDirectoryChangesW buffer ended the watch and the final write was never reloaded. An overflow now schedules a reload, as Pi does for a change without a filename, and keeps watching; any other error still closes the watcher.
- User impact: Lost or altered data
- Platform: Windows
- Fixed in: Unreleased
- Fix commits:
8a88c34d51(2026-10-26) - Tests:
tui/theme_watcher_upstream_test.go::TestThemeWatcherOtherErrorsCloseNativeWatcher,tui/theme_watcher_upstream_test.go::TestThemeWatcherOverflowReloadsAndKeepsWatching - Release note: On Windows, a custom theme edited in a rapid burst of writes no longer stays on a stale version. A burst that overflowed the native change buffer made the watcher treat the overflow as a failure and close its watch, so writes after the overflow were never seen and the theme kept an older version of the file (seen as
accent="#123456" want="#000064"in Windows CI, only while the machine was loaded). An overflow now reloads the theme file, as Pi reloads on a change without a filename, and the watcher keeps watching; any other watch failure still closes it, as in Pi.
BF-8a9bb40
fix(ai): Anthropic retry-delay message and Codex error body without an error member
- Source: PiG's own defect
- Symptom: fix(ai): Anthropic retry-delay message and Codex error body without an error member
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
8a9bb40f7b(2026-10-10) - Tests: None recorded.
BF-8ab5a89
A long session no longer holds each tool result twice
- Source: PiG's own defect
- Symptom: A long session no longer holds each tool result twice.
- Root cause: Pi's session holds the message object the agent appended (session-manager.ts appendMessage pushes it into fileEntries), so a tool result is in memory once. appendMessageEntry kept the appended message only when its record decoded DeepEqual to it, and a tool result never does: its record decodes Details to their JSON (orderedjson.Value), while the tool gave a typed value (ReadDetails, BashDetails, EditToolDetails, ...) or an extension a map. So every tool result was held twice, as the agent's message and as the record's decode, text included.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
06c408c21d(2026-10-11),8ab5a89874(2026-10-11),eab3caf769(2026-10-11) - Tests:
internal/codingagent/session_store_test.go::TestAppendedMessageIsHeldOnceWhenItsRecordDecodesToIt,internal/codingagent/session_store_test.go::TestAppendedToolResultWithTypedDetailsIsHeldOnce,internal/codingagent/tools/truncation_share_test.go::TestTruncatedResultDetailsShareTheReturnedText - Release note: A long session no longer holds each tool result twice. The session now keeps the tool result the agent produced instead of a second copy decoded from its record, and a truncated
read,ls,find,greporbashresult keeps its kept output once instead of again in its truncation details. In a 75-prompt session of file reads (about 4.2M tokens of context), memory at the end drops from about 245 MB to about 170 MB.
BF-8ab86c8
RPC get_state sent right after new_session, fork, clone, switch_session or export_html now…
- Source: PiG's own defect
- Symptom: RPC
get_statesent right afternew_session,fork,clone,switch_sessionorexport_htmlnow answers first from the old Session, as in Pi; PiG still answers the replacement when stdin ends, which Pi does not (D146). - Root cause: One ordering mechanism, rpcAwaitInOrder on the executor's event-loop task queue, serves both awaited operations that Pi resumes in request order: - set_model's auth checks run concurrently and settle in input order. Two pipelined set_models used to apply in completion order, so the final model was wrong in 1 of 12 runs. - Session replacements chain: each starts after the previous one applied and answers after it, so the last one requested decides the final Session. Concurrent replacements raced in Runner.BindCommandActions and hung at stdin end about one run in two. - A fork or clone requested while an earlier replacement is pending reads its entry from the Session that was current when it…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8ab86c8219(2026-10-10) - Tests:
coding/cli/rpc_replacement_order_test.go::TestRPCCloneAfterNewSessionOfOneBatchUsesTheRequestingSession,coding/cli/rpc_replacement_order_test.go::TestRPCReplacementStartsAfterItsBatchWasDispatched,coding/cli/rpc_replacement_order_test.go::TestRPCReplacementsOfOneBatchRunInRequestOrder,coding/cli/rpc_replacement_order_test.go::TestRPCSessionReplacementAnswersAfterALaterCommandOfItsBatch,coding/cli/rpc_replacement_order_test.go::TestRPCSessionReplacementsOfOneBatchSettleInDispatchOrder,coding/cli/rpc_resume_integration_test.go::TestRPCModeSessionReplacementAndExport,coding/cli/rpc_set_model_pipeline_test.go::TestRPCPipelinedSetModelsApplyInInputOrder,test/parity/scenarios/rpc/55-rpc-replacement-concurrent-get-state.toml - Release note: RPC
get_statesent right afternew_session,fork,clone,switch_sessionorexport_htmlnow answers first from the old Session, as in Pi; PiG still answers the replacement when stdin ends, which Pi does not (D146).
BF-8b75dc0
Config selector (pig config): every key that reaches the search input re-filters and resets the selection…
- Source: PiG's own defect
- Symptom: Config selector (
pig config): every key that reaches the search input re-filters and resets the selection to the first item even when the query is unchanged, Tab no longer edits the search when project mode is unavailable, and the search is a callback-mode Input so Enter no longer freezes it, all as in Pi'sResourceList.handleInput(tui/config_selector_oracle_test.go, 648 probes against pinned Pi). - Root cause: Every callback with its arguments, the callback count after each key, and every frame's text are compared; Pi's callbacks reached include onThemePreview, onThemeChange, onModelThinkingLevelChange/Remove and 30 more. Pi has no PiG 'Mask secret input' row (D80), so the driver inserts that row and its callback into a temp copy of Pi's settings-selector.js at Pig's position. One real difference: Pi's WarningSettingsSubmenu shows '(this.state.anthropicExtraUsage ?? true)' and always writes the toggled value; Pig's submenu used the Go zero value, so a warning the settings never set showed as off and its first toggle reported {} (the Set flag was never set). It now defaults to on and marks the…
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d1f258f335(2026-10-06),082355f166(2026-10-07),0aba908b97(2026-10-07),0ae3fcf17c(2026-10-07),4058f34548(2026-10-07),4d041349a7(2026-10-07),56a0400b9c(2026-10-07),5b0e90985b(2026-10-07),623553262c(2026-10-07),70e1841cd8(2026-10-07),8b75dc031c(2026-10-07),a7be906e71(2026-10-07),c8a90a61db(2026-10-07),c8e3d86378(2026-10-07),ccaade27af(2026-10-07),f2a2b3409b(2026-10-07) - Tests:
internal/codingagent/extension_dialog_remapping_test.go::TestExtensionDialogRemappingThroughOwnerLoop,internal/codingagent/hidden_thinking_label_test.go::TestExtensionHiddenThinkingLabelReachesAssistantBlocks,internal/codingagent/remote_overlay_production_review_test.go::TestRemoteOverlayProductionRejectsCachedTerminalWidth,internal/codingagent/scoped_models_oracle_test.go::TestScopedModelsSelectorMatchesPi,internal/codingagent/session_selector_oracle_test.go::TestSessionSelectorInputMatchesPi,internal/codingagent/settings_selector_oracle_test.go::TestSettingsSelectorMatchesPi,internal/codingagent/settings_submenu_oracle_test.go::TestSettingsSubmenusMatchPi,internal/codingagent/thinking_selector_oracle_test.go::TestThinkingSelectorInputMatchesPiand 29 more in docs/bug-ledger.json - Release note: Config selector (
pig config): every key that reaches the search input re-filters and resets the selection to the first item even when the query is unchanged, Tab no longer edits the search when project mode is unavailable, and the search is a callback-mode Input so Enter no longer freezes it, all as in Pi'sResourceList.handleInput(tui/config_selector_oracle_test.go, 648 probes against pinned Pi).
BF-8b8f379
A pi-ai call made while a module loads is answered in every extension host: SDK sessions, the extension…
- Source: PiG's own defect
- Symptom: A pi-ai call made while a module loads is answered in every extension host: SDK sessions, the extension validation, the authentication inspection and the login preview no longer hang on it, and any extension call with no handler now gets an error result instead of silence.
- Root cause: Host.runCall dropped a call that no case took when the Host had neither a UI bridge nor a call hook, so a module that awaited a pi-ai call at load waited for ever in an SDK session (DiscoverAndLoadExtensions, DefaultResourceLoader), the extension validation and the authentication inspection. A call no handler takes is now an error result. A Host with no bridge streams a load-time pi-ai call through SetEarlyModelStream, installed by the coding package from the same EarlyModelStream the CLI Host's bridge uses, and the login preview's own bridge installs it too.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8b8f379483(2026-10-11),e9e9cf8816(2026-10-11) - Tests:
coding/cli/load_time_completion_loaders_test.go::TestAuthContributionInspectionAnswersLoadTimeCompletions,coding/cli/load_time_completion_loaders_test.go::TestAuthContributionLoadByNameAnswersLoadTimeCompletions,coding/cli/load_time_completion_loaders_test.go::TestExtensionLoginPreviewAnswersLoadTimeCompletions,coding/cli/load_time_completion_loaders_test.go::TestExtensionValidationAnswersLoadTimeCompletions,coding/extension/host/subprocess/runtime_node_model_runtime_test.go::TestHostAnswersACallItHasNoHandlerFor,coding/extension/host/subprocess/runtime_node_model_runtime_test.go::TestNodeLoadTimeCompletionAnswersBeforeRegistration,coding/load_time_completion_test.go::TestDefaultResourceLoaderAnswersLoadTimeCompletions,coding/load_time_completion_test.go::TestDiscoverAndLoadExtensionsAnswersLoadTimeCompletionsand 1 more in docs/bug-ledger.json - Release note: A pi-ai call made while a module loads is answered in every extension host: SDK sessions, the extension validation, the authentication inspection and the login preview no longer hang on it, and any extension call with no handler now gets an error result instead of silence.
BF-8b9e467
PIG_PROFILE=metrics reports the exact cumulative allocation counts
- Source: PiG's own defect
- Symptom: This fix it missed allocations still held in the runtime's per-processor caches, so a record could be as much as half a megabyte low, depending on whether a garbage collection happened to run shortly before exit.
- Root cause: PIG_PROFILE=metrics read /gc/heap/allocs through runtime/metrics, which does not flush the per-P mcaches. A span still cached keeps its allocations out of the counter until a refill or a collection, so the record undercounted by up to ~500 KB depending on whether a GC happened to run shortly before exit. That made process.rpc_startup.bytes bimodal: 2.79 MB on a quiet host and 3.38 MB under load. The high value is the real one. CollectMetrics now flushes with ReadMemStats before it reads the counters (TestCollectMetricsCountsAllocations InCachedSpans; it was red 4/5 before).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8b9e467f20(2026-10-12) - Tests:
ai/registry_index_test.go::TestRegistryIndexesMatchPerModelAppendInCatalogOrder,internal/profiling/metrics_test.go::TestCollectMetricsCountsAllocationsInCachedSpans - Release note:
PIG_PROFILE=metricsreports the exact cumulative allocation counts. Before this fix it missed allocations still held in the runtime's per-processor caches, so a record could be as much as half a megabyte low, depending on whether a garbage collection happened to run shortly before exit.
BF-8bc7c8f
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
8bc7c8f3f0(2026-10-10) - Tests:
mcp/oauth/callback_server_options_test.go::TestOAuthCallbackServerNamesTheRedirectMixUpAndReturnsTheContextCause
BF-8c3f5a1
fix(tui): a finished block's hidden thinking run keeps no streaming memos
- Source: PiG's own defect
- Symptom: fix(tui): a finished block's hidden thinking run keeps no streaming memos
- Root cause: An earlier change keeps a finished Markdown's streaming memos until its next render when its text was not drawn since the last delta. A thinking run hidden while it streams (Ctrl+T or a click) shows the hidden-thinking label instead, so its Markdown never renders again and kept the memos for the session.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
8c3f5a1959(2026-10-13) - Tests:
tui/assistant_message_hidden_thinking_memos_test.go::TestFinishedBlockKeepsNoMemosForAHiddenThinkingRun
BF-8c5a7a4
The Go, Python and Rust extension SDKs' serializeConversation (SerializeConversation…
- Source: PiG's own defect
- Symptom: The Go, Python and Rust extension SDKs'
serializeConversation(SerializeConversation,serialize_conversation) omits thinking blocks, as Pi 1.2.0's does and the Go host already did: an assistant message prints only its visible text and tool calls, with no[Assistant thinking]line. - Root cause: The Go host (internal/codingagent/compaction) already omits it; the Go, Python and Rust SDKs still printed it.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8c5a7a4e32(2026-10-13) - Tests: None recorded.
- Release note: The Go, Python and Rust extension SDKs'
serializeConversation(SerializeConversation,serialize_conversation) omits thinking blocks, as Pi 1.2.0's does and the Go host already did: an assistant message prints only its visible text and tool calls, with no[Assistant thinking]line.
BF-8c7bacf
delta.AssertValidOp checks path safety as Pi's assertValidOp does: it rejects a negative, fractional…
- Source: PiG's own defect
- Symptom: It accepted them, and only
ApplyImmutablerejected them while applying. - Root cause:
- AssertValidOp never checked path safety: it accepted [-1], [1.5], [true], [null] and nested segments that Pi's assertPathArg rejects. - Its messages carried an "invalid delta operation: " prefix and wrong names ("t payload" for Pi's "t shape", "invalid ... path" for "path is empty"). - ApplyImmutable rejected a numeric segment on an object; Pi sets that key: applyImmutable({a:1}, [["s",[1],2]]) is {"1":2,"a":1}. - The public path_error test expected ["a","b","c"] where Pi reports the unresolvable container ["a","b"].
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1d27f9097e(2026-10-07),1f339c7ef2(2026-10-07),229e0b3983(2026-10-07),88d190453c(2026-10-07),1f5a782c0a(2026-10-08),2f86645d6b(2026-10-08),33c35623be(2026-10-08),453b3b354c(2026-10-08),5355335e3a(2026-10-08),5dd595fb87(2026-10-08),6ca5fde3a9(2026-10-08),6cd85b3523(2026-10-08),9d34ce6e4f(2026-10-08),d4e4a82ed9(2026-10-08),f735020719(2026-10-08),8c7bacff5b(2026-10-09),9b505ead89(2026-10-09),cb15f9d6b6(2026-10-09) - Tests:
chord/delta/apply_immutable_upstream_test.go::TestCheckedImmutableOperationApplication,chord/delta/apply_immutable_upstream_test.go::TestImmutableApplicationComplexity,chord/delta/apply_numeric_key_test.go::TestApplyImmutableSetsANumericSegmentOnAnObjectAsAKey,chord/delta/apply_pi_oracle_test.go::TestApplyResolvesArraySegmentsInUpstreamOrder,chord/delta/apply_pi_oracle_test.go::TestTruncateInsideASurrogatePairKeepsTheLowHalf,chord/delta/delta_pi_differential_test.go::TestDeltaMatchesPiOnRandomCases,chord/delta/delta_upstream_test.go::TestDeltaArrayIndexSafety,chord/delta/delta_upstream_test.go::TestDeltaCanonicalStringsand 55 more in docs/bug-ledger.json - Release note:
delta.AssertValidOpchecks path safety as Pi'sassertValidOpdoes: it rejects a negative, fractional, boolean, null or nested path segment, and__proto__,constructororprototype. Before, it accepted them, and onlyApplyImmutablerejected them while applying. Its messages are Pi's (path is empty,t shape).
BF-8c7bacf-2
delta.ApplyImmutable sets a numeric path segment on an object as that key, as Pi does
- Source: PiG's own defect
- Symptom: It reported
unresolvable path. - Root cause:
- AssertValidOp never checked path safety: it accepted [-1], [1.5], [true], [null] and nested segments that Pi's assertPathArg rejects. - Its messages carried an "invalid delta operation: " prefix and wrong names ("t payload" for Pi's "t shape", "invalid ... path" for "path is empty"). - ApplyImmutable rejected a numeric segment on an object; Pi sets that key: applyImmutable({a:1}, [["s",[1],2]]) is {"1":2,"a":1}. - The public path_error test expected ["a","b","c"] where Pi reports the unresolvable container ["a","b"].
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5355335e3a(2026-10-08),9d34ce6e4f(2026-10-08),8c7bacff5b(2026-10-09),9b505ead89(2026-10-09),cb15f9d6b6(2026-10-09) - Tests:
chord/delta/apply_immutable_upstream_test.go::TestCheckedImmutableOperationApplication,chord/delta/apply_immutable_upstream_test.go::TestImmutableApplicationComplexity,chord/delta/apply_numeric_key_test.go::TestApplyImmutableSetsANumericSegmentOnAnObjectAsAKey,chord/delta/apply_pi_oracle_test.go::TestApplyResolvesArraySegmentsInUpstreamOrder,chord/delta/apply_pi_oracle_test.go::TestTruncateInsideASurrogatePairKeepsTheLowHalf,chord/delta/delta_pi_differential_test.go::TestDeltaMatchesPiOnRandomCases,chord/delta/delta_upstream_test.go::TestDeltaArrayIndexSafety,chord/delta/delta_upstream_test.go::TestDeltaCanonicalStringsand 28 more in docs/bug-ledger.json - Release note:
delta.ApplyImmutablesets a numeric path segment on an object as that key, as Pi does. Before, it reportedunresolvable path.
BF-8c7bacf-3
PathError and UnsafePathError messages print paths as JSON.stringify does: <, >, &, U+2028 and…
- Source: PiG's own defect
- Symptom:
PathErrorandUnsafePathErrormessages print paths asJSON.stringifydoes:<,>,&, U+2028 and U+2029 stay unescaped and-0prints as0. - Root cause:
- AssertValidOp never checked path safety: it accepted [-1], [1.5], [true], [null] and nested segments that Pi's assertPathArg rejects. - Its messages carried an "invalid delta operation: " prefix and wrong names ("t payload" for Pi's "t shape", "invalid ... path" for "path is empty"). - ApplyImmutable rejected a numeric segment on an object; Pi sets that key: applyImmutable({a:1}, [["s",[1],2]]) is {"1":2,"a":1}. - The public path_error test expected ["a","b","c"] where Pi reports the unresolvable container ["a","b"].
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2f86645d6b(2026-10-08),8c7bacff5b(2026-10-09) - Tests:
chord/delta/apply_immutable_upstream_test.go::TestCheckedImmutableOperationApplication,chord/delta/apply_immutable_upstream_test.go::TestImmutableApplicationComplexity,chord/delta/apply_numeric_key_test.go::TestApplyImmutableSetsANumericSegmentOnAnObjectAsAKey,chord/delta/apply_pi_oracle_test.go::TestApplyResolvesArraySegmentsInUpstreamOrder,chord/delta/apply_pi_oracle_test.go::TestTruncateInsideASurrogatePairKeepsTheLowHalf,chord/delta/delta_pi_differential_test.go::TestDeltaMatchesPiOnRandomCases,chord/delta/delta_upstream_test.go::TestDeltaArrayIndexSafety,chord/delta/delta_upstream_test.go::TestDeltaCanonicalStringsand 18 more in docs/bug-ledger.json - Release note:
PathErrorandUnsafePathErrormessages print paths asJSON.stringifydoes:<,>,&, U+2028 and U+2029 stay unescaped and-0prints as0.
BF-8d008eb
fix(interactive): paint the requested first frame before the color wait returns
- Source: PiG's own defect
- Symptom: fix(interactive): paint the requested first frame before the color wait returns
- Root cause: The first frame has nothing to throttle against, so TUI.start's request now reaches the owner loop at once (RequestImmediateRender), and the startup color wait paints a frame already handed to the owner loop before it returns. A terminal that answers at once no longer leaves the first frame waiting for the rest of Init.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
8d008ebed6(2026-10-13) - Tests: None recorded.
BF-8d23cfc
Saving a setting keeps the key order of the existing settings.json, at the top level and inside a patched…
- Source: PiG's own defect
- Symptom: A replaced setting stays where it was and a new setting is added at the end; the file used to be rewritten with its top-level keys sorted.
- Root cause: Pi persists { ...currentFileSettings } with the modified fields assigned onto it and JSON.stringify(merged, null, 2) (settings-manager.ts:715-732), so the file's keys keep their order. saveSettingsPatch re-encoded a Go map and sorted the top-level keys. Regression: TestSaveSettingsPatchKeepsTheFileKeyOrder, TestSaveSettingsPatchReplacesInPlace.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8d23cfcee3(2026-10-06) - Tests:
internal/codingagent/settings_key_order_test.go::TestSaveSettingsPatchKeepsTheFileKeyOrder,internal/codingagent/settings_key_order_test.go::TestSaveSettingsPatchReplacesInPlace - Release note: Saving a setting keeps the key order of the existing
settings.json, at the top level and inside a patched object, as Pi does. A replaced setting stays where it was and a new setting is added at the end; the file used to be rewritten with its top-level keys sorted.
BF-8eb1202
An OpenAI Chat Completions chunk with a member of an unexpected type (a number for delta.content…
- Source: PiG's own defect
- Symptom: An OpenAI Chat Completions chunk with a member of an unexpected type (a number for
delta.content,delta.tool_callsorfunction.arguments, a non-objectchoicesordelta, a non-stringdelta.roleor tool calltype, a falsycustom, a tool call element that is not an object) is read as Pi's JavaScript reads it: the member is ignored, tool-call arguments append their string form, atool_callsstring makes one empty tool call per character, any othertool_callsvalue that is not an array ends the turn withchoice.delta.tool_calls is not iterable, non-empty array content (or an object with a positivelength) appends its string form, a truthyfinish_reasonthat is not a string ends the turn withProvider finish_reason: <its string form>, a usage that is not an object resets the counts to 0, a falsy count or a details member that is not an object reads as 0, and a prompt count that is not a number counts as its Number() when that is an integer. - Root cause: Pi reads
event.message.idandevent.message.usage.input_tokens(anthropic-messages.ts:666, :679),event.content_block.type(:690),event.delta.type(:737) andevent.delta.stop_reason(:818) without a guard, so a null or absent member throwsCannot read properties of null (reading 'x')(orof undefined) and the turn ends with that error; the response id and model are recorded before the usage read throws. PiG's typed decode read each as an empty value and streamed on. - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),5078f73100(2026-10-11),5932542c21(2026-10-11),8eb1202528(2026-10-11),e1abc6c615(2026-10-11) - Tests:
ai/anthropic_event_member_type_oracle_test.go::TestAnthropicEventMistypedMembersMatchPi,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-start.toml - Release note: An OpenAI Chat Completions chunk with a member of an unexpected type (a number for
delta.content,delta.tool_callsorfunction.arguments, a non-objectchoicesordelta, a non-stringdelta.roleor tool calltype, a falsycustom, a tool call element that is not an object) is read as Pi's JavaScript reads it: the member is ignored, tool-call arguments append their string form, atool_callsstring makes one empty tool call per character, any othertool_callsvalue that is not an array ends the turn withchoice.delta.tool_calls is not iterable, non-empty array content (or an object with a positivelength) appends its string form, a truthyfinish_reasonthat is not a string ends the turn withProvider finish_reason: <its string form>, a usage that is not an object resets the counts to 0, a falsy count or a details member that is not an object reads as 0, and a prompt count that is not a number counts as its Number() when that is an integer. An Anthropiccontent_block_deltawith a missing or non-numeric index matches no block and its text, thinking, partial-JSON and signature members append their string form. An Anthropicmessage_start,content_block_startormessage_deltais read the same way: a usage, delta or content block that is not an object reads as empty, a falsymessage_startcount is 0, a block's text, thinking or signature that is not a string starts with its string form, a model that is not a string becomes the response model's string form, and a truthystop_reasonthat is not a string ends the turn withUnhandled stop reason: <its string form>. A null or missing message, message usage, content block or delta ends the turn with Pi'sCannot read properties of null(orundefined) error instead of streaming on. Other mistyped members still end the turn with the decode error.
BF-8f45099
Compaction summaries, branch summaries and bug reports omit thinking blocks (Pi 15e7bdc1, Pi #9602): replayed…
- Source: PiG's own defect
- Symptom: Compaction summaries, branch summaries and bug reports omit thinking blocks (Pi 15e7bdc1, Pi #9602): replayed thinking is often not in the live context, so including it could make the summarization request larger than the context it summarizes and compaction fail on every attempt.
- Root cause: Pi (fix(coding-agent,durable): omit thinking from compaction summaries): replayed thinking is often not in the live context (radius drops it, Anthropic drops blocks that do not match the request), so including it made a summarization request larger than the context it summarizes (a ~996K of 1M session produced 1.38M-token requests that failed on every attempt). serializeConversation writes only the visible text, tool calls and tool results. Compaction, branch summaries and bug reports all go through it. PiG's D141 (keep the first and last 1000 UTF-16 units of a thinking-only message) is retired: its remove-when ("Pi bounds thinking-only messages in compaction summaries") holds, and…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
8f450993c6(2026-10-11) - Tests:
coding/cli/rpc_compaction_thinking_only_test.go::TestRPCCompactionSurvivesThinkingOnlyMessagesTheRequestsOmit,internal/codingagent/compaction/serialization_oracle_test.go::TestSerializeConversationMatchesPiOracle,internal/codingagent/compaction/serialization_upstream_test.go::TestSerializeConversationUpstream,internal/codingagent/compaction/utils_test.go::TestSerializeConversation_AssistantThinkingIsOmitted,test/docs-drift/bug_ledger_test.go::TestBugLedgerTestsExist,test/docs-drift/changelog_fragments_test.go::TestChangelogFragmentsFollowTheRules,test/docs-drift/divergences_test.go::TestDivergenceLedgerEqualsItsRecordFiles,test/docs-drift/divergences_test.go::TestEveryActiveDivergenceIsInTheBundleand 1 more in docs/bug-ledger.json - Release note: Compaction summaries, branch summaries and bug reports omit thinking blocks (Pi 15e7bdc1, Pi #9602): replayed thinking is often not in the live context, so including it could make the summarization request larger than the context it summarizes and compaction fail on every attempt.
BF-8f88d43
On Windows, an extension's exec no longer returns no output for a command that printed, when the machine is…
- Source: PiG's own defect
- Symptom: On Windows, an extension's
execno longer returns no output for a command that printed, when the machine is too busy to run PiG's pipe reader within 100 ms of the command's exit. - Root cause: extension.ExecCommand ended the post-exit wait when the 100 ms grace of Pi's waitForChildProcess elapsed, because on every platform it told childwait that a descendant might still hold the output pipes. On Windows nothing else can answer (a pipe cannot be asked without waiting behind the reader that is blocked in reading it: PeekNamedPipe blocked behind ReadFile on windows-latest), so a reader that a busy machine had not run for the grace lost what the child wrote before it exited, as the bash tool's shell did on Unix. The bash tool's shell does not have this on Windows: it starts suspended and joins a job object, and an empty job tells Wait that no process holds the pipe.
- User impact: Wrong output or ordering
- Platform: Windows
- Fixed in: Unreleased
- Fix commits:
8f88d43fa5(2026-10-24) - Tests:
coding/extension/exec_drain_test.go::TestExecCommandKeepsTheOutputOfAChildThatExitedBeforeItsReadersRan - Release note: On Windows, an extension's
execno longer returns no output for a command that printed, when the machine is too busy to run PiG's pipe reader within 100 ms of the command's exit. The command now starts suspended and joins a job object before it can start another process, as the bash tool's shell already did, so PiG knows when no process holds the output pipes any more and reads them to the end instead of dropping what the command had written before it exited.
BF-905f3d0
A trap of the codemode module on a Worker no longer loses the output the script printed before it: the host…
- Source: PiG's own defect
- Symptom: A script that printed 100,000 one-character items used to trap the module on its heap and now completes (a batch of the same items took 1.06 s, against 1.56 s with the module holding them; the exchange count and the other timings are unchanged).
- Root cause: The Go behavior backend found the module trapping at its heap cap on a script at the output limits. After the hand-written exchange JSON () the 16 MiB case passes at 64 MiB; 100,000 output items cost about 460 bytes each in the module and need the cap at 96 MiB (the whole sandbox.test.ts table, 52 behaviors, passes on the Worker host). The earlier message's sync.Pool explanation was not verified; the measured cause is the copies. exchangeSizeHint moves to the tinygo file (unused elsewhere).
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7494507f32(2026-10-10),7cfe779980(2026-10-10),07a474224f(2026-10-11),123e9549ea(2026-10-11),6e1f6d5a4b(2026-10-11),6f72a8fe03(2026-10-11),905f3d001f(2026-10-11),982b1fccc4(2026-10-11),a1ed417e3a(2026-10-11),aa9704868f(2026-10-11),aec01937f1(2026-10-11),af95a496fb(2026-10-11),d40e75c067(2026-10-11) - Tests:
codemode/cloudflare/cloudflare_test.go::TestCreateInlineSandboxRunsWithTheEntrysModule,codemode/cloudflare/cloudflare_test.go::TestNewSandboxDurableObjectHasPisDefaults,codemode/cloudflare/cloudflare_test.go::TestTheObjectsLimitsCapEveryRequest,codemode/cloudflare/cloudflare_test.go::TestTheWorkerObjectHasTheSameDefaults,codemode/engine_exports_test.go::TestQJSExportsMatchTheWorkerHost,codemode/resources_linux_test.go::TestConcurrentExecutionsReturnMemoryAndDescriptors,codemode/serve_encode_test.go::TestARemoteServerThatHandsItsOutputToTheHostCountsIt,codemode/serve_encode_test.go::TestAppendExchangeCountsTheHostedOutputand 8 more in docs/bug-ledger.json - Release note: A trap of the codemode module on a Worker no longer loses the output the script printed before it: the host holds the output items, and the crash message is delivered behind them, as Pi delivers its buffered output with a crash. A script that printed 100,000 one-character items used to trap the module on its heap and now completes (a batch of the same items took 1.06 s, against 1.56 s with the module holding them; the exchange count and the other timings are unchanged).
BF-9088293
The Azure OpenAI base URL is parsed and serialized with the WHATWG URL parser, as Pi does: hosts are…
- Source: PiG's own defect
- Symptom: The Azure OpenAI base URL is parsed and serialized with the WHATWG URL parser, as Pi does: hosts are lowercased and converted from IDN form, any scheme Pi's
new URLaccepts is accepted (and one it rejects is rejected), and the/openai/v1rewrite works on the normalized host and path. - Root cause: The Go function required '://' and a host, which Pi's new URL does not. It now parses with nodeurl.ParseURL, tests the Azure host on the normalized hostname, rewrites through WithPathname and WithSearch(""), and serializes with the new URL.Href.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
15a5c0b670(2026-10-09),2c04c1445a(2026-10-09),31f4517fbe(2026-10-09),7c4d874438(2026-10-09),90882933f6(2026-10-09),c4d51d5c1a(2026-10-09),d80c1cd150(2026-10-09) - Tests:
ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,ai/azure_config_oracle_test.go::TestAzureBaseURLNormalizationMatchesPi,ai/codex_account_id_oracle_test.go::TestCodexAccountIDMatchesPi,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,ai/radius_urls_oracle_test.go::TestRadiusURLsMatchPiOnSeededProbes,ai/trusted_url_oracle_test.go::TestTrustedVerificationURLsMatchPi,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,internal/nodeurl/url_oracle_test.go::TestParseMatchesNodeURL - Release note: The Azure OpenAI base URL is parsed and serialized with the WHATWG URL parser, as Pi does: hosts are lowercased and converted from IDN form, any scheme Pi's
new URLaccepts is accepted (and one it rejects is rejected), and the/openai/v1rewrite works on the normalized host and path.
BF-909581e
The terminal no longer stays in mouse-tracking, focus-reporting, Kitty keyboard, bracketed-paste or…
- Source: A fix Pi shipped that PiG ported
- Symptom: The terminal no longer stays in mouse-tracking, focus-reporting, Kitty keyboard, bracketed-paste or alternate-screen mode when pig exits through a path that skips the normal teardown (a direct exit, an emergency exit, an off-loop crash, a panic unwinding through
Main). - Root cause: Port Pi (#11170): Pi's process 'exit' handler writes the mode resets synchronously when stop() did not run. Go has no exit hook, so tui.RestoreTerminalOnExit is that handler and every exit path reachable while a TUI runs (coding/cli exitProcess; emergencyTerminalExit, handleInterruptSignal, uncaughtOffLoop) calls it before os.Exit. Order matches TuiAltScreen: end synchronized output, mouse off, autowrap on, cursor, progress clear, bracketed paste off, Kitty pop, modifyOtherKeys off, then alternate-screen exit and cursor show; cooked mode is restored as libuv does. Registered terminals and alt screens leave on normal stop, so nothing is written twice.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
909581ef0b(2026-10-20) - Tests:
coding/cli/exit_restore_paths_test.go::TestEveryExitPathReachableWhileATUIRunsRestoresTheTerminal,tui/terminal_exit_restore_test.go::TestExitRestoreFixture,tui/terminal_exit_restore_test.go::TestRestoreTerminalOnExitMainScreenWritesOnceAndRestoresCookedMode,tui/terminal_exit_restore_test.go::TestTerminalRestoreOnProcessExit,tui/terminal_exit_restore_test.go::TestTerminalRestoreOnProcessExitDoesNotRepeatAfterStop - Release note: The terminal no longer stays in mouse-tracking, focus-reporting, Kitty keyboard, bracketed-paste or alternate-screen mode when pig exits through a path that skips the normal teardown (a direct exit, an emergency exit, an off-loop crash, a panic unwinding through
Main). The modes are restored in Pi's order, once, and not again after a normal stop (Pi 9df819de, #11170).
BF-909a2e2
MCP OAuth metadata documents with a member whose name differs from a modeled one only in case (for example…
- Source: PiG's own defect
- Symptom: MCP OAuth metadata documents with a member whose name differs from a modeled one only in case (for example
Scopes_Supported) are no longer rejected; the member is kept as an unmodeled member, as in Pi. - Root cause: Pi packages/mcp/src/oauth/types.ts:198-209 parseClientInformation spreads the whole response, and flow.ts:125-138 selectClientAuthMethod reads token_endpoint_auth_method from it. ParseClientInformation decoded the whole struct with encoding/json, which fails on the first ill-typed member and lost every typed member with it (a registration with "contacts":"bad" lost token_endpoint_auth_method, so the token request used client_secret_basic instead of the registered client_secret_post), and matched members without regard to case. Each member now decodes on its own under its exact name.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5654e71185(2026-10-05),909a2e20e6(2026-10-05) - Tests:
mcp/oauth/client_information_test.go::TestParseClientInformationKeepsTypedMembersBesideAnIllTypedOne,mcp/oauth/client_information_test.go::TestParseClientInformationReadsExactMemberNames,mcp/oauth/upstream_oracle_test.go::TestBuildAuthorizationServerDiscoveryURLsMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseAuthorizationServerMetadataMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseClientInformationMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseOAuthTokensMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseProtectedResourceMetadataMatchesPi,mcp/oauth/upstream_oracle_test.go::TestParseWWWAuthenticateMatchesPiand 3 more in docs/bug-ledger.json - Release note: MCP OAuth metadata documents with a member whose name differs from a modeled one only in case (for example
Scopes_Supported) are no longer rejected; the member is kept as an unmodeled member, as in Pi.
BF-90c1f0f
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: TestBuiltinToolCardsMatchPi and TestBuiltinToolCardsRandomMatchPi compared the bash card's Elapsed/Took row, which both PiG (shellRenderCall and shellRenderResult) and Pi (renderers/bash.ts createShellRenderers) read from the wall clock between the start event and the result render. Under CPU load either side could cross a 50 ms rounding step the other did not, so the row flipped between 0.0s and 0.1s (or more).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
90c1f0f52c(2026-10-13) - Tests:
internal/codingagent/builtin_tool_cards_oracle_test.go::TestBuiltinToolCardsMatchPi,internal/codingagent/builtin_tool_cards_oracle_test.go::TestBuiltinToolCardsRandomMatchPi
BF-9101bc8
Bedrock model matching follows Pi: separator runs in a model id or display name become a single - (so "Opus…
- Source: PiG's own defect
- Symptom: Bedrock model matching follows Pi: separator runs in a model id or display name become a single
-(so "Opus 4.6" and "sonnet...5" match the adaptive-thinking and caching families), no-break spaces count as separators, and a model counts as Anthropic Claude only when its id carries ananthropic.claudeoranthropic/claudeprefix or its display name mentions claude. - Root cause: getModelMatchCandidates, supportsAdaptiveThinking, supportsNativeXhighEffort, supportsThinkingBlockBinding, isAnthropicClaudeModel and supportsPromptCaching are cut from Pi's source (testdata/bedrockmodel_match.mjs) and run on 4500 seeded model id/name pairs. 3035 differed (red before). Causes: (1) bedrockModelMatchCandidates trimmed values, skipped empty ones and de-duplicated, and replaced each of ' .:' separately where Pi rewrites every run of \s, '_', '.', ':' to one '-', so 'Opus 4.6', 'sonnet...5' and 'Haiku::5' never reached the 'opus-4-6'/'sonnet-5'/'haiku-5' families and no-break spaces or NEL were not separators; (2) isAnthropicClaudeBedrockModel tested 'claude' on the model…
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7c4d874438(2026-10-09),9101bc880b(2026-10-09) - Tests:
ai/bedrock_model_match_oracle_test.go::TestBedrockModelMatchingMatchesPi,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,ai/overflow_oracle_test.go::TestContextOverflowMatchesPiOnSeededMessages,internal/evals/plan_oracle_test.go::TestPlanMatchesPiOnSeededProbes - Release note: Bedrock model matching follows Pi: separator runs in a model id or display name become a single
-(so "Opus 4.6" and "sonnet...5" match the adaptive-thinking and caching families), no-break spaces count as separators, and a model counts as Anthropic Claude only when its id carries ananthropic.claudeoranthropic/claudeprefix or its display name mentions claude.
BF-918f21c
formatThousands (tui/compaction_summary.go, compaction-summary-message.ts:40…
- Source: PiG's own defect
- Symptom:
formatThousands(tui/compaction_summary.go, compaction-summary-message.ts:40tokensBefore.toLocaleString()) wrote a negative count as-,123(the separator rule counted the minus sign as a digit); it now keeps the sign out of the grouping. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
918f21c5e8(2026-10-11) - Tests:
tui/compaction_summary_thousands_test.go::TestFormatThousandsMatchesPi - Release note:
formatThousands(tui/compaction_summary.go, compaction-summary-message.ts:40tokensBefore.toLocaleString()) wrote a negative count as-,123(the separator rule counted the minus sign as a digit); it now keeps the sign out of the grouping. New live-Pi testTestFormatThousandsMatchesPi(28 values, both signs, up to ±2^53; red with the sign handling off or the grouping shifted). A negativetokensBeforeonly comes from a hand-edited session file.
BF-919575a
A JSON syntax error reports what V8's JSON.parse reports in two cases it got wrong: a number or string…
- Source: PiG's own defect
- Symptom: A JSON syntax error reports what V8's
JSON.parsereports in two cases it got wrong: a number or string where atrue,falseornullliteral breaks off (t1,fals") is "Unexpected number" or "Unexpected string in JSON at position N", and a backslash followed by a character above U+00FF in a string is "Unexpected token" rather than "Bad escaped character". - Root cause: redactJsonValue is JSON.parse(JSON.stringify(value)); RedactBugReportJSON kept the number text it decoded with UseNumber, so -0, 1e21 and a 30-digit integer reached the report as -0, 1e21 and the digits where Pi writes 0, 1e+21 and 1.+29. TestBugReportRedactionMatchesPi compares 1500 URLs (schemes, nested schemes, userinfo, IPv6 and IDN hosts, dot segments, percent-encoding, repeated and mixed-case query keys, fragments, non-URLs) and 800 JSON values (sensitive keys in snake, kebab and camel case, nested containers, null members) with Pi's redactUrl and redactJsonValue; the URLs all matched, the numbers did not (red first). Numbers are now canonicalised through jsonstringify.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
52573667c1(2026-10-09),579f07df0e(2026-10-09),d07038cb99(2026-10-09),919575a79c(2026-10-10) - Tests:
ai/classifier_answers_oracle_test.go::TestClassifierAnswersMatchPi,ai/system_one_shared_test.go::TestSystemOneSharedFailureMessages,internal/codingagent/bug_report_redact_oracle_test.go::TestBugReportRedactionMatchesPi,internal/codingagent/bug_report_redact_oracle_test.go::TestBugReportRedactionNormalizesNumbersLikeJSONStringify,internal/codingagent/session_file_bytes_oracle_test.go::TestSessionFileBytesMatchPi,internal/jsonparse/json_parse_oracle_test.go::TestValidateMatchesNodeJSONParse,internal/sessionentry/entry_test.go::TestBranchSummaryEntryKeepsTheMemberOrderOfDetails - Release note: A JSON syntax error reports what V8's
JSON.parsereports in two cases it got wrong: a number or string where atrue,falseornullliteral breaks off (t1,fals") is "Unexpected number" or "Unexpected string in JSON at position N", and a backslash followed by a character above U+00FF in a string is "Unexpected token" rather than "Bad escaped character".
BF-91e912a
Two PiG versions that share a config root (two installed versions, or PiG beside a Piglet Binary) no longer…
- Source: PiG's own defect
- Symptom: Two PiG versions that share a config root (two installed versions, or PiG beside a Piglet Binary) no longer restage each other's extension SDKs at every start or delete each other's extension builds.
- Root cause: Two PiG builds that share a config root (two installed versions, or stock PiG beside a Piglet Binary) staged into the same state/pigsdk/sdk, sdk-py and sdk-rs, so every switch restaged all three and pruned every extension build made against the other build's SDK, and a Python extension of one build could import the other build's SDK.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),e8cc072521(2026-10-08),395c46cbf2(2026-10-09),852652e3ae(2026-10-09),da99854d1b(2026-10-09),91e912aa90(2026-10-10) - Tests:
chord/delta/own_key_order_test.go::TestDiffRevisionsVisitsArrayIndexKeysFirstInNumericOrder,coding/cli/piglet_verify_cache_startup_test.go::TestSignedPigletBinaryRunsWhenItsVerifyCacheCannotBeWritten,coding/cli/readonly_config_root_binary_test.go::TestReadOnlyConfigRootStartsQuietlyAndNamesOnlyDocsThatExist,coding/cli/startup_sdk_test.go::TestStartupSDKStageIsQuietOnAReadOnlyConfigRoot,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,coding/extension/pigsdk/pigsdk_stage_test.go::TestStagingLeavesAnotherBuildsStagingTrees,coding/extension/pigsdk/pigsdk_version_share_test.go::TestStagingEvictsLeastRecentlyUsedSDKsAndTheirBuilds,coding/extension/pigsdk/pigsdk_version_share_test.go::TestStagingKeepsAnotherBuildsSDKAndItsBuildsand 12 more in docs/bug-ledger.json - Release note: Two PiG versions that share a config root (two installed versions, or PiG beside a Piglet Binary) no longer restage each other's extension SDKs at every start or delete each other's extension builds. Each version stages its SDKs into its own directory under
state/pigsdk, named for the SDK's content hash, and reads only that directory, so a Python extension also always runs against its own version's SDK. After staging, PiG keeps the 4 most recently used SDK directories per language and drops only the extension builds whose SDK is gone.pig reload --sdk-pathprints the running version's directory, andpig diagnoseshows it.
BF-91f9946
OpenRouter image generation reads the chat completions body as Pi does, checked against the pinned Pi in a…
- Source: PiG's own defect
- Symptom: OpenRouter image generation reads the chat completions body as Pi does, checked against the pinned Pi in a new oracle test: a body with no
choices(OpenRouter sometimes answers 200 with an error object) fails with Pi's TypeError instead of returning an empty success, content, image and usage members of other types are skipped or fail with the same message, and an unparseable body reports the JSON syntax error. - Root cause: TestOpenRouterImagesEdgeResponsesMatchPi (new, 150 bodies, with its own Pi driver) showed that a 200 body without choices returned an empty success where Pi throws Cannot read properties of undefined (reading '0'), and that content, image and usage members of another type failed the decode where Pi skips them or throws a specific TypeError. The body is now walked untyped in Pi's order, so the id, usage and text read before a failing member stay on the result.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
91f99469d5(2026-10-11) - Tests:
ai/openrouter_images_edge_oracle_test.go::TestOpenRouterImagesEdgeResponsesMatchPi - Release note: OpenRouter image generation reads the chat completions body as Pi does, checked against the pinned Pi in a new oracle test: a body with no
choices(OpenRouter sometimes answers 200 with an error object) fails with Pi's TypeError instead of returning an empty success, content, image and usage members of other types are skipped or fail with the same message, and an unparseable body reports the JSON syntax error.
BF-928e15c
A listener that fails while agent_start is dispatched now ends the run as in Pi (failure messages, no…
- Source: PiG's own defect
- Symptom: A listener that fails while agent_start is dispatched now ends the run as in Pi (failure messages, no turn_start or prompt), and the failure message carries the model's api (agent.ts handleRunFailure).
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A listener that fails while agent_start is dispatched now ends the run as in Pi (failure messages, no turn_start or prompt), and the failure message carries the model's api (agent.ts handleRunFailure).
BF-928e15c-10
Constrained-sampling errors quote tool and property names as Pi does (raw text between double quotes, not…
- Source: PiG's own defect
- Symptom: Constrained-sampling errors quote tool and property names as Pi does (raw text between double quotes, not Go-escaped), and the strict JSON-schema conversion treats an explicit
nullproperties/itemsand atypearray holding non-string elements like Pi when deciding whether a union variant is an object or array. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Constrained-sampling errors quote tool and property names as Pi does (raw text between double quotes, not Go-escaped), and the strict JSON-schema conversion treats an explicit
nullproperties/itemsand atypearray holding non-string elements like Pi when deciding whether a union variant is an object or array.
BF-928e15c-11
GitHub Copilot model-catalog fetches now report a failed response as Pi does (`<status> <reason>: <untrimmed…
- Source: PiG's own defect
- Symptom: GitHub Copilot model-catalog fetches now report a failed response as Pi does (
<status> <reason>: <untrimmed body>), report a malformed catalog asInvalid Copilot models response, and fail on a body that cannot be read, while a policy POST that is not rate limited no longer fails because its body cannot be read. - Root cause: GitHub Copilot model-catalog fetches now report a failed response as Pi does (
<status> <reason>: <untrimmed body>), report a malformed catalog asInvalid Copilot models response, and fail on a body that cannot be read, while a policy POST that is not rate limited no longer fails because its body cannot be read. - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests:
coding/extension/host/subprocess/runtime_node_sdk_bundle_test.go::TestNodeLibraryBundlesRegenerateExactly,coding/extension/host/subprocess/runtime_node_sdk_bundle_test.go::TestNodeSDKBundleRegeneratesExactly - Release note: GitHub Copilot model-catalog fetches now report a failed response as Pi does (
<status> <reason>: <untrimmed body>), report a malformed catalog asInvalid Copilot models response, and fail on a body that cannot be read, while a policy POST that is not rate limited no longer fails because its body cannot be read.
BF-928e15c-12
GitHub Copilot login now reports failed HTTP responses as Pi does (<status> <reason>: <body>, not `HTTP…
- Source: PiG's own defect
- Symptom: GitHub Copilot login now reports failed HTTP responses as Pi does (
<status> <reason>: <body>, notHTTP <status>: <trimmed body>), reads a device-flowerrorthat is an empty string as a failed device flow, reads numbers past the double range as JavaScript does, and reads ill-formed UTF-8 or a truncated body asfetchdoes. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: GitHub Copilot login now reports failed HTTP responses as Pi does (
<status> <reason>: <body>, notHTTP <status>: <trimmed body>), reads a device-flowerrorthat is an empty string as a failed device flow, reads numbers past the double range as JavaScript does, and reads ill-formed UTF-8 or a truncated body asfetchdoes.
BF-928e15c-13
GitHub Copilot, ChatGPT and OpenAI Codex logins report what Pi reports when a server's response cannot be…
- Source: PiG's own defect
- Symptom: GitHub Copilot, ChatGPT and OpenAI Codex logins report what Pi reports when a server's response cannot be used: JavaScript's SyntaxError text for a body that is not JSON,
terminatedfor a body that is cut off, the server's own reason phrase for an empty error body, and a Copilot device-flowerror_descriptionof any JSON type printed as JavaScript prints it (for example5or[object Object]). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: GitHub Copilot, ChatGPT and OpenAI Codex logins report what Pi reports when a server's response cannot be used: JavaScript's SyntaxError text for a body that is not JSON,
terminatedfor a body that is cut off, the server's own reason phrase for an empty error body, and a Copilot device-flowerror_descriptionof any JSON type printed as JavaScript prints it (for example5or[object Object]).
BF-928e15c-14
GitHub Copilot login sends the browser to the verification URL exactly as Pi does (new URL(...).href: the…
- Source: PiG's own defect
- Symptom: GitHub Copilot login sends the browser to the verification URL exactly as Pi does (
new URL(...).href: the default port dropped), and a device-flow failure appends a non-stringerror_descriptionas Pi prints it. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: GitHub Copilot login sends the browser to the verification URL exactly as Pi does (
new URL(...).href: the default port dropped), and a device-flow failure appends a non-stringerror_descriptionas Pi prints it.
BF-928e15c-15
Device-code login polling now does its time arithmetic in floating-point milliseconds as Pi does, so a NaN…
- Source: PiG's own defect
- Symptom: Device-code login polling now does its time arithmetic in floating-point milliseconds as Pi does, so a NaN, infinite or very large interval or expiry waits as Node's
setTimeoutwould instead of overflowing a Go duration. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Device-code login polling now does its time arithmetic in floating-point milliseconds as Pi does, so a NaN, infinite or very large interval or expiry waits as Node's
setTimeoutwould instead of overflowing a Go duration.
BF-928e15c-16
A durable directory reader now refuses a maxEntries above Number.MAX_SAFE_INTEGER with an invalid…
- Source: PiG's own defect
- Symptom: A durable directory reader now refuses a
maxEntriesaboveNumber.MAX_SAFE_INTEGERwith an invalidFileError, as Pi'sNodeDirReader.nextdoes. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A durable directory reader now refuses a
maxEntriesaboveNumber.MAX_SAFE_INTEGERwith an invalidFileError, as Pi'sNodeDirReader.nextdoes.
BF-928e15c-17
Durable entries keep a present empty edits list ("edits":[]) when they are appended, stored and read…
- Source: PiG's own defect
- Symptom: Durable entries keep a present empty
editslist ("edits":[]) when they are appended, stored and read back, as Pi does. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Durable entries keep a present empty
editslist ("edits":[]) when they are appended, stored and read back, as Pi does. Pig previously dropped it.
BF-928e15c-18
Durable: a JSONL store whose task or submission record carries a status outside the closed set (a damaged…
- Source: PiG's own defect
- Symptom: Durable: a JSONL store whose task or submission record carries a status outside the closed set (a damaged file) now fails to open with "Invalid committed state at sequence N", as Pi's does, instead of loading the record under an unknown status.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Durable: a JSONL store whose task or submission record carries a status outside the closed set (a damaged file) now fails to open with "Invalid committed state at sequence N", as Pi's does, instead of loading the record under an unknown status.
BF-928e15c-19
Fixed Editor.SetText keeping the previous scroll offset: as in Pi, replacing the text puts the view back at…
- Source: PiG's own defect
- Symptom: Fixed
Editor.SetTextkeeping the previous scroll offset: as in Pi, replacing the text puts the view back at the top, so a long editor that was scrolled no longer renders with the wrong rows and scroll indicators aftersetText. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Fixed
Editor.SetTextkeeping the previous scroll offset: as in Pi, replacing the text puts the view back at the top, so a long editor that was scrolled no longer renders with the wrong rows and scroll indicators aftersetText.
BF-928e15c-2
A stored models catalog whose contextWindow or maxTokens is written as 128000.0 or 1.28E5 loads like…
- Source: PiG's own defect
- Symptom: A stored models catalog whose
contextWindowormaxTokensis written as128000.0or1.28E5loads like Pi's JSON.parse reads it, instead of failing to decode the model. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A stored models catalog whose
contextWindowormaxTokensis written as128000.0or1.28E5loads like Pi's JSON.parse reads it, instead of failing to decode the model.
BF-928e15c-20
Fixed frontmatter parsing when the opening --- is followed by a multi-byte character (for example U+0085 or…
- Source: PiG's own defect
- Symptom: Fixed frontmatter parsing when the opening
---is followed by a multi-byte character (for example U+0085 or U+2028) or an astral character instead of a line break: the header now starts after one UTF-16 code unit, as Pi slices it, rather than at byte 4. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Fixed frontmatter parsing when the opening
---is followed by a multi-byte character (for example U+0085 or U+2028) or an astral character instead of a line break: the header now starts after one UTF-16 code unit, as Pi slices it, rather than at byte 4.
BF-928e15c-21
Google streams replace an ill-formed UTF-8 sequence with one U+FFFD per maximal subpart (the WHATWG…
- Source: PiG's own defect
- Symptom: Google streams replace an ill-formed UTF-8 sequence with one U+FFFD per maximal subpart (the WHATWG TextDecoder rule) and drop a leading byte order mark, as Pi's genai SDK does; the decoder used before emitted one replacement character per bad byte.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Google streams replace an ill-formed UTF-8 sequence with one U+FFFD per maximal subpart (the WHATWG TextDecoder rule) and drop a leading byte order mark, as Pi's genai SDK does; the decoder used before emitted one replacement character per bad byte.
BF-928e15c-22
Case-insensitive matching of tool names (Claude Code tool casing), the anthropic-beta, reserved Bedrock and…
- Source: PiG's own defect
- Symptom: Case-insensitive matching of tool names (Claude Code tool casing), the
anthropic-beta, reserved Bedrock and OpenCode session header names, Bedrock GovCloud regions and model ids, the Codex usage-limit error codes, the merging of model and caller header names and the Bedrock data-retention hint now lower-case with JavaScript rules, soİis no longer treated asi. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Case-insensitive matching of tool names (Claude Code tool casing), the
anthropic-beta, reserved Bedrock and OpenCode session header names, Bedrock GovCloud regions and model ids, the Codex usage-limit error codes, the merging of model and caller header names and the Bedrock data-retention hint now lower-case with JavaScript rules, soİis no longer treated asi.
BF-928e15c-23
Kimi Code OAuth login and refresh now read the server's response the way Pi does: a body that is not a JSON…
- Source: PiG's own defect
- Symptom: Kimi Code OAuth login and refresh now read the server's response the way Pi does: a body that is not a JSON object or array counts as absent, a field of an unexpected JSON type is reported as invalid or missing (instead of a Go decode error), an error body is quoted as
JSON.stringifywrites it (member order, number and string forms), error descriptions keep an empty value,slow_downcarries the server'sinterval, a leading byte order mark is ignored, the failure texts match Pi's (Invalid Kimi Code device authorization response,Kimi Code token refresh response missing fields: …, the status and body of a 5xx during login), and a refresh with an empty stored token sends the request as Pi does. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Kimi Code OAuth login and refresh now read the server's response the way Pi does: a body that is not a JSON object or array counts as absent, a field of an unexpected JSON type is reported as invalid or missing (instead of a Go decode error), an error body is quoted as
JSON.stringifywrites it (member order, number and string forms), error descriptions keep an empty value,slow_downcarries the server'sinterval, a leading byte order mark is ignored, the failure texts match Pi's (Invalid Kimi Code device authorization response,Kimi Code token refresh response missing fields: …, the status and body of a 5xx during login), and a refresh with an empty stored token sends the request as Pi does.
BF-928e15c-24
Kimi Code and xAI OAuth decode a response body as fetch does: ill-formed UTF-8 becomes one U+FFFD per maximal…
- Source: PiG's own defect
- Symptom: Kimi Code and xAI OAuth decode a response body as fetch does: ill-formed UTF-8 becomes one U+FFFD per maximal ill-formed subpart (not one per byte, nor one per run), in quoted error bodies and in fields such as
error_description; a body that fails to read (a connection closed mid-body) is never parsed, so a truncated token response is no longer accepted. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Kimi Code and xAI OAuth decode a response body as fetch does: ill-formed UTF-8 becomes one U+FFFD per maximal ill-formed subpart (not one per byte, nor one per run), in quoted error bodies and in fields such as
error_description; a body that fails to read (a connection closed mid-body) is never parsed, so a truncated token response is no longer accepted.
BF-928e15c-25
A tool or command that throws an Error with an empty message reports an empty message in the Node runtime…
- Source: PiG's own defect
- Symptom: A tool or command that throws an
Errorwith an empty message reports an empty message in the Node runtime, as upstream does (err instanceof Error ? err.message : String(err)); it reportedError. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A tool or command that throws an
Errorwith an empty message reports an empty message in the Node runtime, as upstream does (err instanceof Error ? err.message : String(err)); it reportedError.
BF-928e15c-26
The Rust SDK parses every JSON number as the nearest double, as JSON.parse does; it could land one unit in…
- Source: PiG's own defect
- Symptom: The Rust SDK parses every JSON number as the nearest double, as
JSON.parsedoes; it could land one unit in the last place away (123456789012345680000was read as123456789012345670000). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The Rust SDK parses every JSON number as the nearest double, as
JSON.parsedoes; it could land one unit in the last place away (123456789012345680000was read as123456789012345670000).
BF-928e15c-27
The Go SDK writes NaN and infinities as null (and negative zero as 0) in the frames it sends, as…
- Source: PiG's own defect
- Symptom: The Go SDK writes NaN and infinities as
null(and negative zero as0) in the frames it sends, asJSON.stringifyand the Node, Rust and Python SDKs do; a Go tool that returned one used to fail with an encoding error. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The Go SDK writes NaN and infinities as
null(and negative zero as0) in the frames it sends, asJSON.stringifyand the Node, Rust and Python SDKs do; a Go tool that returned one used to fail with an encoding error.
BF-928e15c-28
The Node SDK's messageRole now returns "" for a message whose role is not a string, as the Go, Rust and…
- Source: PiG's own defect
- Symptom: The Node SDK's
messageRolenow returns""for a message whoseroleis not a string, as the Go, Rust and Python SDKs do (it returned the value, e.g.5). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The Node SDK's
messageRolenow returns""for a message whoseroleis not a string, as the Go, Rust and Python SDKs do (it returned the value, e.g.5).
BF-928e15c-29
The Go and Rust SDKs now answer a tool result past the 128 MiB frame limit with a frame too large error, as…
- Source: PiG's own defect
- Symptom: The Go and Rust SDKs now answer a tool result past the 128 MiB frame limit with a
frame too largeerror, as the Node and Python SDKs do; the call used to hang until the run was aborted. - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The Go and Rust SDKs now answer a tool result past the 128 MiB frame limit with a
frame too largeerror, as the Node and Python SDKs do; the call used to hang until the run was aborted.
BF-928e15c-3
Provider HTTP error bodies are decoded like response.text(): ill-formed UTF-8 becomes one U+FFFD per…
- Source: PiG's own defect
- Symptom: Provider HTTP error bodies are decoded like
response.text(): ill-formed UTF-8 becomes one U+FFFD per maximal subpart and a leading byte order mark is dropped, so an error message no longer carries raw invalid bytes (Anthropic, OpenAI completions and Responses, Azure, Google, Mistral, pi-messages, Codex). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Provider HTTP error bodies are decoded like
response.text(): ill-formed UTF-8 becomes one U+FFFD per maximal subpart and a leading byte order mark is dropped, so an error message no longer carries raw invalid bytes (Anthropic, OpenAI completions and Responses, Azure, Google, Mistral, pi-messages, Codex).
BF-928e15c-30
The Python SDK sends NaN and infinities as null (as JSON.stringify does) instead of the bare words…
- Source: PiG's own defect
- Symptom: The Python SDK sends NaN and infinities as
null(asJSON.stringifydoes) instead of the bare wordsNaN/Infinity, which the host refused and which ended the extension's connection. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The Python SDK sends NaN and infinities as
null(asJSON.stringifydoes) instead of the bare wordsNaN/Infinity, which the host refused and which ended the extension's connection.
BF-928e15c-31
The Rust SDK decodes tool arguments, notifications and host-call results nested past 128 levels (up to 4096)…
- Source: PiG's own defect
- Symptom: The Rust SDK decodes tool arguments, notifications and host-call results nested past 128 levels (up to 4096) instead of dropping its host connection.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The Rust SDK decodes tool arguments, notifications and host-call results nested past 128 levels (up to 4096) instead of dropping its host connection.
BF-928e15c-32
The Rust SDK no longer drops its host connection when a frame (a tool argument, event payload or object key)…
- Source: PiG's own defect
- Symptom: The Rust SDK no longer drops its host connection when a frame (a tool argument, event payload or object key) holds an unpaired UTF-16 surrogate; it decodes the unit as U+FFFD, as Go does for a
string. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The Rust SDK no longer drops its host connection when a frame (a tool argument, event payload or object key) holds an unpaired UTF-16 surrogate; it decodes the unit as U+FFFD, as Go does for a
string.
BF-928e15c-33
The login dialog renders its one input at every prompt position until a submit replaces them with the…
- Source: PiG's own defect
- Symptom: The login dialog renders its one input at every prompt position until a submit replaces them with the submitted text, and keeps it after a cancel, as Pi's
LoginDialogComponentdoes; an unanswered earlier prompt no longer renders blank. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The login dialog renders its one input at every prompt position until a submit replaces them with the submitted text, and keeps it after a cancel, as Pi's
LoginDialogComponentdoes; an unanswered earlier prompt no longer renders blank.
BF-928e15c-34
Markdown: a rule written with the list's own marker (* * *) after a blank line ends the list with its blank…
- Source: PiG's own defect
- Symptom: Markdown: a rule written with the list's own marker (
* * *) after a blank line ends the list with its blank row, and a line after a three-level nested list is a lazy line only where each enclosing item allows it, as in Pi. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown: a rule written with the list's own marker (
* * *) after a blank line ends the list with its blank row, and a line after a three-level nested list is a lazy line only where each enclosing item allows it, as in Pi.
BF-928e15c-35
Markdown: a list item line that is a rule (- a, a blank line, - - -; or - * * *) renders as a rule…
- Source: PiG's own defect
- Symptom: Markdown: a list item line that is a rule (
- a, a blank line,- - -; or- * * *) renders as a rule inside the item instead of hanging the renderer. - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown: a list item line that is a rule (
- a, a blank line,- - -; or- * * *) renders as a rule inside the item instead of hanging the renderer.
BF-928e15c-36
Markdown: an indented line that a table delimiter row (---) follows joins the paragraph before it with its…
- Source: PiG's own defect
- Symptom: Markdown: an indented line that a table delimiter row (
---) follows joins the paragraph before it with its indent removed, instead of rendering as a separate code block, as in Pi. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown: an indented line that a table delimiter row (
---) follows joins the paragraph before it with its indent removed, instead of rendering as a separate code block, as in Pi.
BF-928e15c-37
Markdown: a text that ends in one line of spaces after an indented code block, an HTML block or a table no…
- Source: PiG's own defect
- Symptom: Markdown: a text that ends in one line of spaces after an indented code block, an HTML block or a table no longer renders the extra empty row, a fence right after an HTML comment gets no spacing row, and an indented line after an empty quoted line starts a code block in the quote, as in Pi.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown: a text that ends in one line of spaces after an indented code block, an HTML block or a table no longer renders the extra empty row, a fence right after an HTML comment gets no spacing row, and an indented line after an empty quoted line starts a code block in the quote, as in Pi.
BF-928e15c-38
Markdown no longer adds a blank row after a quote or block when only a repeated link definition follows it…
- Source: PiG's own defect
- Symptom: Markdown no longer adds a blank row after a quote or block when only a repeated link definition follows it, and a reference before its definition no longer changes that.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown no longer adds a blank row after a quote or block when only a repeated link definition follows it, and a reference before its definition no longer changes that.
BF-928e15c-39
Markdown: a line that continues a nested list item's paragraph (- a, - b, x) renders at the nested…
- Source: PiG's own defect
- Symptom: Markdown: a line that continues a nested list item's paragraph (
- a,- b,x) renders at the nested item's text column, and a bullet with an empty first line followed by a nested list keeps its empty bullet row, as in Pi. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown: a line that continues a nested list item's paragraph (
- a,- b,x) renders at the nested item's text column, and a bullet with an empty first line followed by a nested list keeps its empty bullet row, as in Pi.
BF-928e15c-4
A Gemini (or Bedrock/native) response streamed in many small chunks no longer hangs the stream…
- Source: PiG's own defect
- Symptom: A Gemini (or Bedrock/native) response streamed in many small chunks no longer hangs the stream intermittently: the body reader goroutine no longer runs the completion that starts the next read.
- Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A Gemini (or Bedrock/native) response streamed in many small chunks no longer hangs the stream intermittently: the body reader goroutine no longer runs the completion that starts the next read.
BF-928e15c-40
Markdown no longer adds an empty row after a table that is too narrow to draw, when the table is inside a…
- Source: PiG's own defect
- Symptom: Markdown no longer adds an empty row after a table that is too narrow to draw, when the table is inside a list item and another block follows it.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown no longer adds an empty row after a table that is too narrow to draw, when the table is inside a list item and another block follows it.
BF-928e15c-41
Markdown: list items whose markers are indented by different amounts (0 to 3 spaces) form one list, so a…
- Source: PiG's own defect
- Symptom: Markdown: list items whose markers are indented by different amounts (0 to 3 spaces) form one list, so a loose list keeps its empty rows between them, as in Pi.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown: list items whose markers are indented by different amounts (0 to 3 spaces) form one list, so a loose list keeps its empty rows between them, as in Pi.
BF-928e15c-42
Markdown rendering shows a horizontal rule (- - -, * * *) inside a nested list item as a rule, as Pi…
- Source: PiG's own defect
- Symptom: Markdown rendering shows a horizontal rule (
- - -,* * *) inside a nested list item as a rule, as Pi does, instead of an empty bullet. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown rendering shows a horizontal rule (
- - -,* * *) inside a nested list item as a rule, as Pi does, instead of an empty bullet.
BF-928e15c-43
Markdown nested lists treat a less-indented continuation line the way Pi does: a line indented to a nested…
- Source: PiG's own defect
- Symptom: Markdown nested lists treat a less-indented continuation line the way Pi does: a line indented to a nested item's content relative to its parent stays in that item, and a
--- | ---line no longer ends the item. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown nested lists treat a less-indented continuation line the way Pi does: a line indented to a nested item's content relative to its parent stays in that item, and a
--- | ---line no longer ends the item.
BF-928e15c-44
Markdown: a blockquote whose last quoted line holds only spaces or tabs (> q, >, x) now renders the…
- Source: PiG's own defect
- Symptom: Markdown: a blockquote whose last quoted line holds only spaces or tabs (
> q,>,x) now renders the lazy line in the same paragraph, with the whitespace line kept as an empty row, as Pi does. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown: a blockquote whose last quoted line holds only spaces or tabs (
> q,>,x) now renders the lazy line in the same paragraph, with the whitespace line kept as an empty row, as Pi does.
BF-928e15c-45
Markdown task lists whose later item is a heading (- [ ] b over ===) keep and strip checkbox markers on…
- Source: PiG's own defect
- Symptom: Markdown task lists whose later item is a heading (
- [ ] bover===) keep and strip checkbox markers on the same items as Pi. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Markdown task lists whose later item is a heading (
- [ ] bover===) keep and strip checkbox markers on the same items as Pi.
BF-928e15c-46
MCP HTTP servers and the OAuth requests of a sign-in now report a connection that fails before any response…
- Source: PiG's own defect
- Symptom: MCP HTTP servers and the OAuth requests of a sign-in now report a connection that fails before any response as
fetch failed, as Pi does (Node fetch rejection), instead of Go'sPost "<url>": dial tcp ...text in/mcp,pi mcp listandpi mcp login. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: MCP HTTP servers and the OAuth requests of a sign-in now report a connection that fails before any response as
fetch failed, as Pi does (Node fetch rejection), instead of Go'sPost "<url>": dial tcp ...text in/mcp,pi mcp listandpi mcp login.
BF-928e15c-47
Meta OAuth login now reads the server's responses as Pi does: an invalid device-authorization response is…
- Source: PiG's own defect
- Symptom: Meta OAuth login now reads the server's responses as Pi does: an invalid device-authorization response is quoted as
JSON.stringifywrites it (an array body as the array, members in the server's order, numbers as JavaScript prints them), a number past the double range is an unusable interval instead of voiding the whole body, a leading byte order mark or ill-formed UTF-8 is read asfetchdoes, and a body that fails to read counts asnull. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Meta OAuth login now reads the server's responses as Pi does: an invalid device-authorization response is quoted as
JSON.stringifywrites it (an array body as the array, members in the server's order, numbers as JavaScript prints them), a number past the double range is an unusable interval instead of voiding the whole body, a leading byte order mark or ill-formed UTF-8 is read asfetchdoes, and a body that fails to read counts asnull.
BF-928e15c-48
Case-insensitive glob matching compares as a JavaScript regular expression does: İ no longer matches i…
- Source: PiG's own defect
- Symptom: Case-insensitive glob matching compares as a JavaScript regular expression does:
İno longer matchesi, the Kelvin sign no longer matchesk, and the Angstrom sign no longer matcheså(the engine lower-cased both sides). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Case-insensitive glob matching compares as a JavaScript regular expression does:
İno longer matchesi, the Kelvin sign no longer matchesk, and the Angstrom sign no longer matcheså(the engine lower-cased both sides).
BF-928e15c-49
OpenAI ChatGPT and Codex OAuth token failures with an empty body report the reason phrase the server sent…
- Source: PiG's own defect
- Symptom: OpenAI ChatGPT and Codex OAuth token failures with an empty body report the reason phrase the server sent (
response.statusText), as Pi does, instead of the canonical text for the status code. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: OpenAI ChatGPT and Codex OAuth token failures with an empty body report the reason phrase the server sent (
response.statusText), as Pi does, instead of the canonical text for the status code.
BF-928e15c-5
Sign in with ChatGPT now reads the token endpoint's responses as Pi does: a failed response quotes the body…
- Source: PiG's own defect
- Symptom: Sign in with ChatGPT now reads the token endpoint's responses as Pi does: a failed response quotes the body decoded as
fetchdoes (an unreadable body falls back to the status text), a body that is not JSON or cannot be read fails the request instead of reporting that it is not an object, and a leading byte order mark or ill-formed UTF-8 is read asfetchdoes. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Sign in with ChatGPT now reads the token endpoint's responses as Pi does: a failed response quotes the body decoded as
fetchdoes (an unreadable body falls back to the status text), a body that is not JSON or cannot be read fails the request instead of reporting that it is not an object, and a leading byte order mark or ill-formed UTF-8 is read asfetchdoes.
BF-928e15c-50
OpenRouter OAuth key exchange now ignores a leading byte order mark in the response body, as Pi's…
- Source: PiG's own defect
- Symptom: OpenRouter OAuth key exchange now ignores a leading byte order mark in the response body, as Pi's
Response.json()does. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: OpenRouter OAuth key exchange now ignores a leading byte order mark in the response body, as Pi's
Response.json()does.
BF-928e15c-51
Fullscreen double-click selects the word Pi selects: words come from the same ICU segmentation as…
- Source: PiG's own defect
- Symptom: Fullscreen double-click selects the word Pi selects: words come from the same ICU segmentation as Intl.Segmenter (Thai, Lao, Khmer and Burmese dictionary words, Devanagari conjuncts, emoji ZWJ sequences), and thinking-block wrapping and Mermaid widths split only between Intl.Segmenter graphemes.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Fullscreen double-click selects the word Pi selects: words come from the same ICU segmentation as Intl.Segmenter (Thai, Lao, Khmer and Burmese dictionary words, Devanagari conjuncts, emoji ZWJ sequences), and thinking-block wrapping and Mermaid widths split only between Intl.Segmenter graphemes.
BF-928e15c-52
Anthropic: a server-requested retry delay above the cap fails with `Server requested Ns retry delay (max…
- Source: PiG's own defect
- Symptom: Anthropic: a server-requested retry delay above the cap fails with
Server requested Ns retry delay (max: Ms). <status> <body>like the SDK's error, not the HTTP client'sPost "<url>": … 500 Internal Server Error.text. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Anthropic: a server-requested retry delay above the cap fails with
Server requested Ns retry delay (max: Ms). <status> <body>like the SDK's error, not the HTTP client'sPost "<url>": … 500 Internal Server Error.text.
BF-928e15c-53
OpenAI Codex: an error body whose error member is absent or falsy (an object without it, an array, null)…
- Source: PiG's own defect
- Symptom: OpenAI Codex: an error body whose
errormember is absent or falsy (an object without it, an array,null) keeps the raw body as the message instead of the usage-limit text. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: OpenAI Codex: an error body whose
errormember is absent or falsy (an object without it, an array,null) keeps the raw body as the message instead of the usage-limit text.
BF-928e15c-54
Provider streams read integers written as 12.0, 1.2e1 or 12E0 (a token count, an index) as JavaScript's…
- Source: PiG's own defect
- Symptom: Provider streams read integers written as
12.0,1.2e1or12E0(a token count, an index) as JavaScript's JSON.parse does; Go's decoder refused them for an integer field and ended the stream with an error. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Provider streams read integers written as
12.0,1.2e1or12E0(a token count, an index) as JavaScript's JSON.parse does; Go's decoder refused them for an integer field and ended the stream with an error. Anthropic, OpenAI completions and Responses (Azure, Codex), Google, Mistral and pi-messages are covered.
BF-928e15c-55
Radius OAuth login now reads the gateway's responses as Pi does: an error body that is a JSON string, number…
- Source: PiG's own defect
- Symptom: Radius OAuth login now reads the gateway's responses as Pi does: an error body that is a JSON string, number or array reports just the HTTP status (not the body text), a
nullbody reports the JavaScript TypeError text, a JSONnullexpires_incounts as zero, device-authorization members of an unexpected JSON type report the missing-fields error, and ill-formed UTF-8 or a truncated body is read asfetchdoes. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Radius OAuth login now reads the gateway's responses as Pi does: an error body that is a JSON string, number or array reports just the HTTP status (not the body text), a
nullbody reports the JavaScript TypeError text, a JSONnullexpires_incounts as zero, device-authorization members of an unexpected JSON type report the missing-fields error, and ill-formed UTF-8 or a truncated body is read asfetchdoes.
BF-928e15c-56
Radius OAuth login again signs in when the gateway's token response has no refresh token, as Pi does
- Source: PiG's own defect
- Symptom: Radius OAuth login again signs in when the gateway's token response has no refresh token, as Pi does.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Radius OAuth login again signs in when the gateway's token response has no refresh token, as Pi does. A success response that is not JSON, or whose body is cut off, reports the same message Pi shows (JavaScript's SyntaxError text, or
terminated), and so does a discovery error whose body is cut off. Every OAuth login reads a body that starts with two byte order marks as Node's fetch does: both are dropped.
BF-928e15c-57
A provider request whose URL path Go cannot parse no longer sends the URL fragment to the server; fetch never…
- Source: PiG's own defect
- Symptom: A provider request whose URL path Go cannot parse no longer sends the URL fragment to the server; fetch never sends it.
- Root cause: Not recorded.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A provider request whose URL path Go cannot parse no longer sends the URL fragment to the server; fetch never sends it. OpenAI Codex again reports a base URL with credentials with fetch's own message.
BF-928e15c-58
The Anthropic stream reader takes an event's type only from a member named exactly type, as Pi does; a…
- Source: PiG's own defect
- Symptom: The Anthropic stream reader takes an event's type only from a member named exactly
type, as Pi does; a member such asTYPEno longer marks the start or end of a message. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The Anthropic stream reader takes an event's type only from a member named exactly
type, as Pi does; a member such asTYPEno longer marks the start or end of a message.
BF-928e15c-59
A provider HTTP error body that starts with two byte order marks reads as response.text() reads it: both…
- Source: PiG's own defect
- Symptom: A provider HTTP error body that starts with two byte order marks reads as
response.text()reads it: both marks are dropped, so a JSON error message is found. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A provider HTTP error body that starts with two byte order marks reads as
response.text()reads it: both marks are dropped, so a JSON error message is found. Pig dropped one.
BF-928e15c-6
The codemode-store session entry lists the keys of its set object as Pi does (array-index keys ascending…
- Source: PiG's own defect
- Symptom: The
codemode-storesession entry lists the keys of itssetobject as Pi does (array-index keys ascending, then the others in the order the script first wrote them) instead of in sorted order (execute.ts, host.ts parseStoreWrites). - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The
codemode-storesession entry lists the keys of itssetobject as Pi does (array-index keys ascending, then the others in the order the script first wrote them) instead of in sorted order (execute.ts, host.ts parseStoreWrites).
BF-928e15c-60
A models.json whose contextWindow, maxTokens or a model override writes an integer as 128000.0 or…
- Source: PiG's own defect
- Symptom: A
models.jsonwhosecontextWindow,maxTokensor a model override writes an integer as128000.0or1.28E5loads as Pi's JSON.parse reads it; Pig refused the whole file with "Failed to parse models.json". - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A
models.jsonwhosecontextWindow,maxTokensor a model override writes an integer as128000.0or1.28E5loads as Pi's JSON.parse reads it; Pig refused the whole file with "Failed to parse models.json".
BF-928e15c-61
Classifier (typesafe-system-one, llama.cpp) and Radius gateway config responses are decoded as…
- Source: PiG's own defect
- Symptom: Classifier (typesafe-system-one, llama.cpp) and Radius gateway config responses are decoded as
response.text()andresponse.json()decode them: two leading byte order marks are dropped and ill-formed UTF-8 becomes U+FFFD. - Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Classifier (typesafe-system-one, llama.cpp) and Radius gateway config responses are decoded as
response.text()andresponse.json()decode them: two leading byte order marks are dropped and ill-formed UTF-8 becomes U+FFFD. A classifier answer behind two marks failed with a JSON syntax error, and a Radius config behind a mark was reported invalid.
BF-928e15c-62
Amazon Bedrock streams read integers written as 12.0 or 1e0 (a content block index, a token count) as the…
- Source: PiG's own defect
- Symptom: Amazon Bedrock streams read integers written as
12.0or1e0(a content block index, a token count) as the AWS SDK does after JSON.parse; Pig ended the stream with a decode error. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Amazon Bedrock streams read integers written as
12.0or1e0(a content block index, a token count) as the AWS SDK does after JSON.parse; Pig ended the stream with a decode error.
BF-928e15c-63
An OpenAI Codex HTTP error with an empty body reports the reason phrase the server sent, or "Request failed"…
- Source: PiG's own defect
- Symptom: An OpenAI Codex HTTP error with an empty body reports the reason phrase the server sent, or "Request failed" when it sent none, as Pi does; Pig used Go's status text, which is empty for a code such as 529.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: An OpenAI Codex HTTP error with an empty body reports the reason phrase the server sent, or "Request failed" when it sent none, as Pi does; Pig used Go's status text, which is empty for a code such as 529. A Codex error body behind three or four byte order marks is now read as Pi reads it, through
response.text()twice.
BF-928e15c-64
Print mode (-p) now prints the partial text, writes a warning to stderr and exits 1 when the final response…
- Source: PiG's own defect
- Symptom: Print mode (
-p) now prints the partial text, writes a warning to stderr and exits 1 when the final response was cut off at the model's output token limit, instead of exiting 0 without a warning. - Root cause: Pi's shutdown() stops the TUI, awaits runtimeHost.dispose() (session_shutdown), prints the resume command when the session file exists and calls process.exit(0); it never awaits the session_start, session_before_switch or session_before_fork handler that asked for the shutdown (interactive-mode.ts:2020-2024, 4251-4290).
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),928e15cd57(2026-10-14) - Tests:
internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-start.toml - Release note: Print mode (
-p) now prints the partial text, writes a warning to stderr and exits 1 when the final response was cut off at the model's output token limit, instead of exiting 0 without a warning.
BF-928e15c-65
Delayed terminal replies (OSC color, XTVersion, Kitty graphics) that arrive in pieces are no longer typed…
- Source: PiG's own defect
- Symptom: Delayed terminal replies (OSC color, XTVersion, Kitty graphics) that arrive in pieces are no longer typed into the editor or read as Ctrl+G; batched Alt+]/P/_ keystrokes still time out as keys (Pi 1.2.0 stdin-buffer).
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Delayed terminal replies (OSC color, XTVersion, Kitty graphics) that arrive in pieces are no longer typed into the editor or read as Ctrl+G; batched Alt+]/P/_ keystrokes still time out as keys (Pi 1.2.0 stdin-buffer).
BF-928e15c-66
In the alternate screen, wheel input that a focused overlay does not handle scrolls the transcript (Pi 1.2.0…
- Source: PiG's own defect
- Symptom: In the alternate screen, wheel input that a focused overlay does not handle scrolls the transcript (Pi 1.2.0 #10769).
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: In the alternate screen, wheel input that a focused overlay does not handle scrolls the transcript (Pi 1.2.0 #10769).
BF-928e15c-67
A session file with invalid UTF-8 inside a record loads with one U+FFFD per maximal invalid subpart, as…
- Source: PiG's own defect
- Symptom: A session file with invalid UTF-8 inside a record loads with one U+FFFD per maximal invalid subpart, as Node's UTF-8 decoder reads it, instead of one per byte.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A session file with invalid UTF-8 inside a record loads with one U+FFFD per maximal invalid subpart, as Node's UTF-8 decoder reads it, instead of one per byte.
BF-928e15c-68
A system message's sections object now enumerates integer-like section names first, in ascending order…
- Source: PiG's own defect
- Symptom: A system message's
sectionsobject now enumerates integer-like section names first, in ascending order, when read from or written to JSON, as a JavaScript object does, so prompt section replay and planned section patches keep Pi's order. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A system message's
sectionsobject now enumerates integer-like section names first, in ascending order, when read from or written to JSON, as a JavaScript object does, so prompt section replay and planned section patches keep Pi's order.
BF-928e15c-69
A configured thinking budget of zero (or below) now wins over the default in the OpenAI-compatible, Google…
- Source: PiG's own defect
- Symptom: A configured thinking budget of zero (or below) now wins over the default in the OpenAI-compatible, Google and Bedrock Claude request builders and the shared simple-options helpers, as in Pi.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A configured thinking budget of zero (or below) now wins over the default in the OpenAI-compatible, Google and Bedrock Claude request builders and the shared simple-options helpers, as in Pi. This applies to
thinkingBudgetsin settings and to an extension model request'sthinkingBudgets.ai.ThinkingBudgetsgains aSetfield andai.NewThinkingBudgets, and reads and writes Pi's{"minimal":n,...}JSON object with only the configured levels. A Claude budget of zero is floored to 1024 and is not lowered further by the context clamp (thinkingBudgetTokens || 1024).
BF-928e15c-7
OpenAI Codex device-code login now reads the server's responses the way Pi does: a field of an unexpected…
- Source: PiG's own defect
- Symptom: OpenAI Codex device-code login now reads the server's responses the way Pi does: a field of an unexpected JSON type reports the response as invalid (quoted as
JSON.stringifywrites it, not as the raw body) instead of a Go decode error,intervalstrings follow JavaScriptNumber()(hex, exponent and whitespace forms), any 2xx status counts as success, error bodies and a leading byte order mark are read asResponse.text()andResponse.json()do, and an unparsable body fails the login instead of reporting a failed poll. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: OpenAI Codex device-code login now reads the server's responses the way Pi does: a field of an unexpected JSON type reports the response as invalid (quoted as
JSON.stringifywrites it, not as the raw body) instead of a Go decode error,intervalstrings follow JavaScriptNumber()(hex, exponent and whitespace forms), any 2xx status counts as success, error bodies and a leading byte order mark are read asResponse.text()andResponse.json()do, and an unparsable body fails the login instead of reporting a failed poll.
BF-928e15c-70
A tool call whose arguments hold a key with an escaped lone surrogate ("\ud800a") keeps that key's place in…
- Source: PiG's own defect
- Symptom: A tool call whose arguments hold a key with an escaped lone surrogate (
"\ud800a") keeps that key's place in the member order, instead of moving it last. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A tool call whose arguments hold a key with an escaped lone surrogate (
"\ud800a") keeps that key's place in the member order, instead of moving it last.
BF-928e15c-71
The agent loop now gives a custom stream function Pi's messages (errored and aborted turns kept, no synthetic…
- Source: PiG's own defect
- Symptom: The agent loop now gives a custom stream function Pi's messages (errored and aborted turns kept, no synthetic "No result provided" results); real providers repair orphaned, repeated and out-of-order tool results themselves (D48).
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: The agent loop now gives a custom stream function Pi's messages (errored and aborted turns kept, no synthetic "No result provided" results); real providers repair orphaned, repeated and out-of-order tool results themselves (D48).
BF-928e15c-72
A real provider's tool-flow repair (D48) no longer moves a tool result back to an earlier unanswered call…
- Source: PiG's own defect
- Symptom: A real provider's tool-flow repair (D48) no longer moves a tool result back to an earlier unanswered call that has the same id, and no longer keeps a result in a turn that never made its call.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A real provider's tool-flow repair (D48) no longer moves a tool result back to an earlier unanswered call that has the same id, and no longer keeps a result in a turn that never made its call. A request whose tool flow Pi sends intact reaches the provider unchanged, even when a provider reuses call ids every turn.
BF-928e15c-73
Fixed the session tree selector keeping its cursor inside the rows
- Source: PiG's own defect
- Symptom: Fixed the session tree selector keeping its cursor inside the rows.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Fixed the session tree selector keeping its cursor inside the rows. As in Pi, Up and Page Down over an empty filtered list leave the selection at -1 (Down leaves it at 1), and a later filter change keeps it there until a row has been selected: the list then shows
(0/N)with no highlighted row.
BF-928e15c-74
SettingsList draws Pi's bytes: its scroll indicator, description and hint rows are no longer padded to the…
- Source: PiG's own defect
- Symptom: SettingsList draws Pi's bytes: its scroll indicator, description and hint rows are no longer padded to the width, and a row or hint that does not fit ends in
...as pi-tui's truncateToWidth ends it. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: SettingsList draws Pi's bytes: its scroll indicator, description and hint rows are no longer padded to the width, and a row or hint that does not fit ends in
...as pi-tui's truncateToWidth ends it.
BF-928e15c-75
A ! command block draws Pi's bytes: two consecutive blocks are separated by one blank row instead of two…
- Source: PiG's own defect
- Symptom: A
!command block draws Pi's bytes: two consecutive blocks are separated by one blank row instead of two, the running hint names thetui.select.cancelkeys (escape/ctrl+c to cancel) as Pi's does, and its rows are padded to the width with Pi's SGR closes. - Root cause: Not recorded.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A
!command block draws Pi's bytes: two consecutive blocks are separated by one blank row instead of two, the running hint names thetui.select.cancelkeys (escape/ctrl+c to cancel) as Pi's does, and its rows are padded to the width with Pi's SGR closes. A quoted line followed by a lazy===stays quoted text, as in Pi, instead of becoming a heading.
BF-928e15c-76
A provider defined only by virtual models reports configured with source environment and label virtual…
- Source: PiG's own defect
- Symptom: A provider defined only by virtual models reports
configuredwith sourceenvironmentand labelvirtualfromModelRegistry.GetProviderAuthStatus/ModelRuntime.GetProviderAuthStatus, andGetProviderAuth/GetAuthresolve it with sourcevirtualinstead of nil, as Pi does. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: A provider defined only by virtual models reports
configuredwith sourceenvironmentand labelvirtualfromModelRegistry.GetProviderAuthStatus/ModelRuntime.GetProviderAuthStatus, andGetProviderAuth/GetAuthresolve it with sourcevirtualinstead of nil, as Pi does.
BF-928e15c-77
Provider order puts a provider of only virtual models where Pi's providerIds does (the place of its…
- Source: PiG's own defect
- Symptom: Provider order puts a provider of only virtual models where Pi's
providerIdsdoes (the place of its models.json ID when that file loaded, else after the others),allModels/allAvailablelist its models there, and the virtual models of a provider with no credentials are not available. - Root cause: Not recorded.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: Provider order puts a provider of only virtual models where Pi's
providerIdsdoes (the place of its models.json ID when that file loaded, else after the others),allModels/allAvailablelist its models there, and the virtual models of a provider with no credentials are not available.
BF-928e15c-78
xAI OAuth login and refresh now read the server's response the way Pi does: a body that is not a JSON object…
- Source: PiG's own defect
- Symptom: xAI OAuth login and refresh now read the server's response the way Pi does: a body that is not a JSON object counts as empty, a field of an unexpected JSON type is reported as that field being invalid (instead of "xAI OAuth returned invalid JSON"), a
refresh_tokenorexpires_inthat is present butnullis rejected rather than treated as absent, a leading byte order mark is ignored, andverification_uri_completeis validated before the required fields. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: xAI OAuth login and refresh now read the server's response the way Pi does: a body that is not a JSON object counts as empty, a field of an unexpected JSON type is reported as that field being invalid (instead of "xAI OAuth returned invalid JSON"), a
refresh_tokenorexpires_inthat is present butnullis rejected rather than treated as absent, a leading byte order mark is ignored, andverification_uri_completeis validated before the required fields. A refresh with an empty stored refresh token now sends the request as Pi does.
BF-928e15c-8
OpenAI Codex and OpenRouter OAuth responses read ill-formed UTF-8 and a body that fails to read as fetch…
- Source: PiG's own defect
- Symptom: OpenAI Codex and OpenRouter OAuth responses read ill-formed UTF-8 and a body that fails to read as
fetchdoes: one U+FFFD per maximal ill-formed subpart, and a truncated body is never parsed (an unreadable error body reads as empty text, an unreadable success body as invalid JSON). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: OpenAI Codex and OpenRouter OAuth responses read ill-formed UTF-8 and a body that fails to read as
fetchdoes: one U+FFFD per maximal ill-formed subpart, and a truncated body is never parsed (an unreadable error body reads as empty text, an unreadable success body as invalid JSON).
BF-928e15c-9
An OpenAI Codex login or refresh whose success response is not JSON reports JavaScript's own message, as Pi…
- Source: PiG's own defect
- Symptom: An OpenAI Codex login or refresh whose success response is not JSON reports JavaScript's own message, as Pi does (for example
Unexpected token 'n', "not json" is not valid JSON, without a PiG prefix), and one whose body is cut off reportsterminated, as Node's fetch does. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
928e15cd57(2026-10-14) - Tests: None recorded.
- Release note: An OpenAI Codex login or refresh whose success response is not JSON reports JavaScript's own message, as Pi does (for example
Unexpected token 'n', "not json" is not valid JSON, without a PiG prefix), and one whose body is cut off reportsterminated, as Node's fetch does.
BF-92da987
Codemode no longer reserves its whole 256 MiB script-memory limit when a session starts: the QuickJS linear…
- Source: PiG's own defect
- Symptom: Codemode no longer reserves its whole 256 MiB script-memory limit when a session starts: the QuickJS linear memory now grows on demand, so an idle session with one small script allocates about 4 MiB instead of about 359 MiB (Go heap held, 321 MiB before, 4 MiB after), while a runaway allocation still fails inside the script at the same limit.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
92da987a37(2026-10-14) - Tests:
codemode/baseline_memory_test.go::TestIdleBaselineMemoryStaysSmall - Release note: Codemode no longer reserves its whole 256 MiB script-memory limit when a session starts: the QuickJS linear memory now grows on demand, so an idle session with one small script allocates about 4 MiB instead of about 359 MiB (Go heap held, 321 MiB before, 4 MiB after), while a runaway allocation still fails inside the script at the same limit. Reported by @kayis-rahman (#211)
BF-93319f4
The terminal UI worked example now describes the typed /model prefix in words
- Source: PiG's own defect
- Symptom: The terminal UI worked example now describes the typed
/modelprefix in words. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
93319f4284(2026-10-11) - Tests:
test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: The terminal UI worked example now describes the typed
/modelprefix in words. A docs check no longer reports a nonexistent command from that page.
BF-93c09d6
A local package source that is a file Node cannot import, such as a packed .tgz, fails to load as in Pi…
- Source: PiG's own defect
- Symptom: A local package source that is a file Node cannot import, such as a packed
.tgz, fails to load as in Pi (Failed to load extension: Unknown file extension ".tgz" for <path>, exit 1) instead of being run as a program and failing with "permission denied". - Root cause: pig install --validate-only on a Package root resolved the root as one Node extension, so a Package whose pi.extensions names a Go directory failed with "declares pi.extensions directories with no extension entry file" although pig package validate and pig install accept it. Validation now loads every extension the Package's discovery names, as an install does.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
93c09d62ff(2026-10-13) - Tests:
coding/cli/extension_validate_command_test.go::TestInstallValidateOnlyValidatesPackageRootMembers,coding/extension/source/resolve_test.go::TestResolveFileWithoutLoaderMatchesNode - Release note: A local package source that is a file Node cannot import, such as a packed
.tgz, fails to load as in Pi (Failed to load extension: Unknown file extension ".tgz" for <path>, exit 1) instead of being run as a program and failing with "permission denied". Like Pi,pig install ./x.tgzstill records the path; Pi does not unpack tarballs either.
BF-93c09d6-2
pig install --validate-only on a Package root validates every extension the Package names in…
- Source: PiG's own defect
- Symptom:
pig install --validate-onlyon a Package root validates every extension the Package names inpi.extensions(or its conventional directories), in any language, as an install loads them. - Root cause: pig install --validate-only on a Package root resolved the root as one Node extension, so a Package whose pi.extensions names a Go directory failed with "declares pi.extensions directories with no extension entry file" although pig package validate and pig install accept it. Validation now loads every extension the Package's discovery names, as an install does.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
93c09d62ff(2026-10-13) - Tests:
coding/cli/extension_validate_command_test.go::TestInstallValidateOnlyValidatesPackageRootMembers,coding/extension/source/resolve_test.go::TestResolveFileWithoutLoaderMatchesNode - Release note:
pig install --validate-onlyon a Package root validates every extension the Package names inpi.extensions(or its conventional directories), in any language, as an install loads them. It no longer treats the root as one Node extension and fails with "declares pi.extensions directories with no extension entry file" for a Go member.
BF-93c5def
A built-in extension named with -e builtin:<name> loads before the extensions from settings and the…
- Source: PiG's own defect
- Symptom: Every built-in loaded after the file extensions.
- Root cause: Tests: TestExplicitBuiltinExtensionsPrecedeTheIndexOrderedOnes now drives the production build (loadProductionExtensionSetIn) and TestExtensionsInLoadOrderWithExplicitPutsExplicitBuiltinsFirst covers the ordering function (none, one, two, a path that did not load). Mutants killed: the build site using the plain order, the function ignoring explicit, the CLI passing no explicit paths. The reload sites share the function and are covered by the unit test, not by a reload-driving test. No generated files in this commit (exports change pig-go.json; the merger regenerates). Signed-off-by: Michael Kinsy mrkinsy5@gmail.com
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
93c5def063(2026-10-10),f5e0378e28(2026-10-10) - Tests:
coding/cli/builtin_extension_order_test.go::TestBuiltinExtensionsLoadInIndexOrderWithLlamaFirst,coding/cli/builtin_extension_order_test.go::TestExplicitBuiltinExtensionsPrecedeTheIndexOrderedOnes,coding/cli/extension_set_builtin_mcp_upstream_test.go::TestUpstreamResourceLoaderNoExtensionsDisablesBuiltinsUnlessLoadedExplicitly,coding/extensionset/extensionset_test.go::TestBuiltinNamesListsARepeatedNameOnce - Release note: A built-in extension named with
-e builtin:<name>loads before the extensions from settings and the extension directories, as in Pi, and keeps that place after/reload. Before, every built-in loaded after the file extensions. The order decides which extension owns a shared command, tool or flag name and the order of event handlers.
BF-93f8b7c
fix(agent): a listener failure at agent_start ends the run as in Pi; failure messages carry the model's api
- Source: PiG's own defect
- Symptom: fix(agent): a listener failure at agent_start ends the run as in Pi; failure messages carry the model's api
- Root cause: Differential test of Pi's Agent class (agent.ts) against Pig's: 3000 seeded programs of prompt/continue/steer/followUp/queue modes/reset/abort, listeners that unsubscribe, act and throw, and scripted turns (text, tool call, error), comparing every operation's error and state and every listener event with its state snapshot.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
93f8b7c250(2026-10-10) - Tests:
agent/agent_class_pi_oracle_test.go::TestAgentClassMatchesPi
BF-94e3498
On Windows, PiG no longer searches C:\etc\pig\piglets or C:\opt\pig\piglet.yaml for a Piglet
- Source: PiG's own defect
- Symptom: On Windows, PiG no longer searches
C:\etc\pig\pigletsorC:\opt\pig\piglet.yamlfor a Piglet. - Root cause: testenv.Start/Run/Output/CombinedOutput start a child in its own process group (Windows: kill-on-close Job Object), bound Wait with WaitDelay, stop the group SIGTERM then SIGKILL at cleanup, and fail the test when a descendant outlives an exited child. The lazy-startup test's logging shim guards against a real tool that re-execs the shim (PID sentinel) and starts the real tool without the shim directory on PATH. The D157 read-only-root test now expects no warning, and the --no-stdin help line no longer trips the strip leak gate.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
5c5c1498a1(2026-10-10),94e3498530(2026-10-10) - Tests:
coding/cli/extension_runtime_lazy_startup_upstream_test.go::TestRuntimeShimStopsWhenTheRealToolReentersIt,coding/cli/piglet_verify_cache_startup_test.go::TestSignedPigletBinaryRunsWhenItsVerifyCacheCannotBeWritten,coding/piglet/system_paths_test.go::TestClassifyLocationNamesTheSystemPigletDirectory,coding/piglet/system_paths_test.go::TestSystemPigletLocationsAreUnixOnly,internal/testenv/spawn_test.go::TestCleanupStopsARunningChild,internal/testenv/spawn_test.go::TestRunStopsADescendantOfAnExitedChild,internal/testenv/spawn_test.go::TestStopEndsAChildThatIgnoresSIGTERM,internal/testenv/spawn_test.go::TestWaitDoesNotBlockOnStdoutHeldByADescendant - Release note: On Windows, PiG no longer searches
C:\etc\pig\pigletsorC:\opt\pig\piglet.yamlfor a Piglet. A rooted path without a volume is drive-relative there, so any local user could plant a Piglet that every other user'spigloaded.
BF-94e6796
A Durable Harness task that went to sleep for a short time, such as the 1 ms backoff before a generation…
- Source: PiG's own defect
- Symptom: A Durable Harness task that went to sleep for a short time, such as the 1 ms backoff before a generation retry, could stay asleep for good on a busy machine, so a
Waiton its submission never returned (a CI run of thedurable/harnesstests hung until the 10-minute timeout). - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
94e6796e8d(2026-10-20, #234) - Tests:
durable/harness/harness_blocked_test.go::TestPublishWakeOwedAfterSeal,durable/harness/scheduler_wake_clock_test.go::TestGenerationRetryWaitSettlesWhenWakeRecomputedAfterBackoffCameDue,durable/harness/scheduler_wake_clock_test.go::TestWakeRecomputedAfterSleepCameDueArmsItsTimer,durable/harness/scheduler_wake_clock_test.go::TestWakeRecomputedAfterSleepCameDueOffersItsAlarm - Release note: A Durable Harness task that went to sleep for a short time, such as the 1 ms backoff before a generation retry, could stay asleep for good on a busy machine, so a
Waiton its submission never returned (a CI run of thedurable/harnesstests hung until the 10-minute timeout). The Harness recomputes when to wake the process in a goroutine that can run milliseconds after the change, and it read the clock when that goroutine ran, not at the change, as Pi's microtask does. A sleep that had been ahead of a scheduling pass was then already due, so no timer was armed for it, and a host'sonWakewas told that work was active instead of the time to wake at; nothing ran the task again. The recomputation now reads the clock of the change that queued it, so the timer is armed (or the time is offered toonWake) and the task wakes.
BF-94f1e6f
fix(coding): a provider of only virtual models is configured with source virtual
- Source: PiG's own defect
- Symptom: fix(coding): a provider of only virtual models is configured with source virtual
- Root cause: Pi's registerVirtualModel marks a virtual-only provider configured (auth source 'virtual') and withVirtualModels resolves its auth with that source: getProviderAuthStatus is {configured, environment, label virtual} and getAuth returns {source virtual}. Pig reported unconfigured and nil. Known remaining (seeds 11, 13): provider list order for a provider of models.json plus virtual models, and allAvailable for an extension provider with virtual models; status after unvirtual keeps Pi's stale virtual entry when models.json exists.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
94f1e6f147(2026-10-10) - Tests: None recorded.
BF-9515bcb
Code mode: a deterministic run's clocks round like Math.round (0.49999999999999994 ms read 1 ms), and a…
- Source: PiG's own defect
- Symptom: Code mode: a deterministic run's clocks round like
Math.round(0.49999999999999994 ms read 1 ms), and a tool declaration's@default/const/enumvalues keep the schema's authored member order instead of sorted members. - Root cause: describe.ts declarationOf hands renderToolSample tool.parameters as authored, and @default/const/enum values are JSON.stringify'd; json.Marshal of the parameters map sorted their members. ParametersJSON keeps the decoded order.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
89b9ec58c4(2026-10-13),9515bcbba5(2026-10-13) - Tests:
codemode/deterministic_internal_test.go::TestTheVirtualClockRoundsLikeMathRound,durable/codemode/describe_test.go::TestDescribeRendersADefaultWithItsAuthoredMemberOrder - Release note: Code mode: a deterministic run's clocks round like
Math.round(0.49999999999999994 ms read 1 ms), and a tool declaration's@default/const/enumvalues keep the schema's authored member order instead of sorted members.
BF-958e80d
A tool call whose arguments hold a number out of float64 range (1e999, -1e999) no longer ends the session…
- Source: PiG's own defect
- Symptom: Pi reads such a number as an infinity, keeps it in the call's arguments, and writes
nullwherever it serializes them (events, the session file, requests); PiG now does the same, for a streamed argument text, a tool call a Go caller or the faux provider supplies, and an Anthropiccontent_block_startinput, which used to end the turn with a decode error. - Root cause: Pi's JSON.parse reads 1e999 as Infinity (and partial-json reads Infinity, -Infinity and NaN), keeps it in a tool call's arguments, and every serialization, JSON.stringify, writes null. A tool call in PiG could hold a non-finite float64 (the streamed text of a provider that hands the cell the parser's number, a Go caller's FauxToolCall, a provider that decodes the whole input) but publishing it copied the arguments through cloneJSONValue, which panicked with "clone validated JSON value: json: unsupported value: +Inf" and ended the session.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
958e80d99f(2026-10-25) - Tests:
ai/tool_call_nonfinite_test.go::FuzzToolArgumentNumberLiterals,ai/tool_call_nonfinite_test.go::TestCellPublishesNonFiniteArguments,ai/tool_call_nonfinite_test.go::TestNonFiniteArgumentsAreValidCloneAndSerializeAsStringify,ai/tool_call_nonfinite_test.go::TestNonFiniteArgumentsDoNotExcuseOtherFaults,ai/tool_call_nonfinite_test.go::TestStreamedNonFiniteArgumentsDoNotPanic,ai/tool_call_nonfinite_test.go::TestStreamedNonFiniteArgumentsMatchPi,ai/transcript_frozen_test.go::TestFrozenContextStillValidatesAndStillCopiesTheCallersTools,ai/transcript_test.go::TestNormalizeContextAcceptsNonFiniteToolArgumentsand 3 more in docs/bug-ledger.json - Release note: A tool call whose arguments hold a number out of float64 range (
1e999,-1e999) no longer ends the session with a panic (clone validated JSON value: json: unsupported value: +Inf). Pi reads such a number as an infinity, keeps it in the call's arguments, and writesnullwherever it serializes them (events, the session file, requests); PiG now does the same, for a streamed argument text, a tool call a Go caller or the faux provider supplies, and an Anthropiccontent_block_startinput, which used to end the turn with a decode error.
BF-95a5f79
RPC mode starts the --tools list active in its own order when it names extension tools, as Pi does
- Source: PiG's own defect
- Symptom: RPC mode starts the
--toolslist active in its own order when it names extension tools, as Pi does. - Root cause: Both fail on. - TestReloadKeepsTheActiveToolOrder: the reload rebuild keeps the active order (agent-session.ts:3666-3669). The interactive /reload test could not catch its loss, because the transcript's initial tool declaration fixes the request order.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
95a5f79e17(2026-10-07) - Tests:
coding/cli/tools_flag_order_test.go::TestReloadKeepsTheToolsFlagOrder,coding/cli/tools_flag_order_test.go::TestToolsFlagOrderReachesTheProviderInEveryMode,coding/cli/tools_flag_order_test.go::TestToolsFlagSelectsToolsInItsOrder,coding/session_tool_registry_port_test.go::TestAllowedToolsStartActiveInTheirOrder,coding/session_tool_registry_port_test.go::TestReloadKeepsTheActiveToolOrder,test/parity/scenarios/rpc/45-rpc-tools-flag-order.toml - Release note: RPC mode starts the
--toolslist active in its own order when it names extension tools, as Pi does.--tools grep,my_tool,readdeclaresgrep,my_tool,readto the model and lists them in that order in the system prompt; RPC mode listed the built-in tools first and appended the extension tools.
BF-95eefe2
openai-completions thinking formats now match Pi (ai/openai.go, openai-completions.ts:885-985 and…
- Source: PiG's own defect
- Symptom: openai-completions thinking formats now match Pi (
ai/openai.go, openai-completions.ts:885-985 and 1034-1075), found by the new live-Pi oracleTestOpenAICompletionsThinkingFormatsPayloadMatchesPi(3,000 probes: everythinkingFormat,compat.chatTemplateKwargs/chatTemplateArgswith scalars, null,$varplaceholders,omitWhenOff, arrays, and modelthinkingLevelMapentries; 32 of 1,200 probes differed before any fix, 4 more after widening the corpus). - Root cause: JSON.parse keeps a member of any type, so Pi keeps a session message whose member has another type than Pi writes, in its entries, branch, context, projection and RPC get_messages (session-manager.ts parseSessionEntryLine, buildSessionContext). PiG's AgentMessage decode failed on such a member and dropped the message from the context, the projection and get_messages: a string timestamp, numeric content, a text block with a numeric text, a usage count written "1", isError "no", a numeric or missing role, and so on.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),95eefe293a(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/openai_completions_thinking_format_oracle_test.go::TestOpenAICompletionsThinkingFormatsPayloadMatchesPi,ai/openai_reasoning_effort_test.go::TestResolveChatTemplateValuesMapsEveryOtherObjectToTheEffort,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgainand 2 more in docs/bug-ledger.json - Release note: openai-completions thinking formats now match Pi (
ai/openai.go, openai-completions.ts:885-985 and 1034-1075), found by the new live-Pi oracleTestOpenAICompletionsThinkingFormatsPayloadMatchesPi(3,000 probes: everythinkingFormat,compat.chatTemplateKwargs/chatTemplateArgswith scalars, null,$varplaceholders,omitWhenOff, arrays, and modelthinkingLevelMapentries; 32 of 1,200 probes differed before any fix, 4 more after widening the corpus). Fixes: (1)chat-templatewith no resolved value sent"chat_template_kwargs": null(a typed nil map in ananyfield defeatedomitempty) where Pi omits the member; (2)resolveChatTemplateValuesdropped a placeholder whose$varis notthinking.enabled/thinking.budgetor athinking.effortthat resolves, where Pi maps every other object (unknown$var,{}, arrays) to the mapped effort; (3) thebasetenformat sentreasoning_effort: "none"when the model had nooffmapping (Pi sends nothing) and sent the clamped level for a level mapped tonull(Pi sends nothing), nowtemplateEffort; (4) theant-lingformat with no requested effort ignored Pi's fall-through to the off-valuereasoning_effort(:970-975); (5) athinkingLevelMapvalue of""was dropped byomitemptywhere Pi sendsreasoning_effort: "", so the request field is now*string. Regressions: the oracle plusTestResolveChatTemplateValuesMapsEveryOtherObjectToTheEffort; 8 mutations red, 1 not applicable../ai -racegreen except the env-onlyTestSDKProvidersRequestURLMatchesSDKandTestOpenAIStreamDecoderMatchesSDK(neednpm ciinai/testdata/sdk). Hot file:ai/openai.gobelongs to go-idioms.
BF-9721c48
An auto, manual or overflow compaction status shown in the editor's top border now takes the editor border…
- Source: PiG's own defect
- Symptom: An auto, manual or overflow compaction status shown in the editor's top border now takes the editor border color, like the working status, as in Pi 1.2.0; it used to keep the accent spinner and muted label.
- Root cause: Branch summary and retry keep accent and muted, as in Pi.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
9721c489d0(2026-10-13) - Tests:
tui/editor_status_test.go::TestEmbeddedCompactionStatusTakesTheEditorBorderColorLikeWorking - Release note: An auto, manual or overflow compaction status shown in the editor's top border now takes the editor border color, like the working status, as in Pi 1.2.0; it used to keep the accent spinner and muted label.
BF-9740507
pig extension upgrade now rewrites a Context.ConfigHome() call in an if or switch init statement and…
- Source: PiG's own defect
- Symptom:
pig extension upgradenow rewrites aContext.ConfigHome()call in aniforswitchinit statement and in a call with other arguments, such asif err := c.save(ctx.ConfigHome()); err != nilorplanDispatch(ctx.Cwd(), ctx.ConfigHome(), name). - Root cause:
- Move a getter out of an if or switch init statement, and move a local Context accessor (Cwd, Mode, HasUI, Width, Height, ToolCallID) that runs before it in the statement out with it, so multi-argument calls such as validateStepRuntime(ctx.Cwd(), ctx.ConfigHome(), x) are rewritten with their order of evaluation kept. - Never discard Context.ConfigHome's error: the old getter could not fail, so in a function that does not return an error the use is listed for the author instead of rewritten to
home, _ :=. Other getters keep their discard, which reproduces the old SDK's zero value on failure. - List a getter's method value passed where the old signature is wanted and classify its…
- Move a getter out of an if or switch init statement, and move a local Context accessor (Cwd, Mode, HasUI, Width, Height, ToolCallID) that runs before it in the statement out with it, so multi-argument calls such as validateStepRuntime(ctx.Cwd(), ctx.ConfigHome(), x) are rewritten with their order of evaluation kept. - Never discard Context.ConfigHome's error: the old getter could not fail, so in a function that does not return an error the use is listed for the author instead of rewritten to
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
974050757c(2026-10-12),d305b76bda(2026-10-12) - Tests:
coding/cli/extension_upgrade_test.go::TestUpgraderRestoresEveryFileWhenTheRebuildFails,coding/cli/extension_upgrade_test.go::TestUpgraderRestoresWrittenFilesWhenAWriteFails,coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideKeepsCurrentStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestRustSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/subprocess/builder_test.go::TestGoNeedsStagedSDK,coding/extension/host/subprocess/builder_test.go::TestRustNeedsStagedSDK,extensions/sdk/upgrade/fixtures_test.go::TestFixturesUpgradeAndBuildand 2 more in docs/bug-ledger.json - Release note:
pig extension upgradenow rewrites aContext.ConfigHome()call in aniforswitchinit statement and in a call with other arguments, such asif err := c.save(ctx.ConfigHome()); err != nilorplanDispatch(ctx.Cwd(), ctx.ConfigHome(), name). ACwd,Mode,HasUI,Width,HeightorToolCallIDcall before the getter moves out with it, so the calls run in the same order as before.
BF-9740507-2
pig extension upgrade no longer discards the error of Context.ConfigHome with `home, _ :=…
- Source: PiG's own defect
- Symptom:
pig extension upgradeno longer discards the error ofContext.ConfigHomewithhome, _ := ctx.ConfigHome(). - Root cause:
- Move a getter out of an if or switch init statement, and move a local Context accessor (Cwd, Mode, HasUI, Width, Height, ToolCallID) that runs before it in the statement out with it, so multi-argument calls such as validateStepRuntime(ctx.Cwd(), ctx.ConfigHome(), x) are rewritten with their order of evaluation kept. - Never discard Context.ConfigHome's error: the old getter could not fail, so in a function that does not return an error the use is listed for the author instead of rewritten to
home, _ :=. Other getters keep their discard, which reproduces the old SDK's zero value on failure. - List a getter's method value passed where the old signature is wanted and classify its…
- Move a getter out of an if or switch init statement, and move a local Context accessor (Cwd, Mode, HasUI, Width, Height, ToolCallID) that runs before it in the statement out with it, so multi-argument calls such as validateStepRuntime(ctx.Cwd(), ctx.ConfigHome(), x) are rewritten with their order of evaluation kept. - Never discard Context.ConfigHome's error: the old getter could not fail, so in a function that does not return an error the use is listed for the author instead of rewritten to
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
974050757c(2026-10-12),d305b76bda(2026-10-12) - Tests:
coding/cli/extension_upgrade_test.go::TestUpgraderRestoresEveryFileWhenTheRebuildFails,coding/cli/extension_upgrade_test.go::TestUpgraderRestoresWrittenFilesWhenAWriteFails,coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideKeepsCurrentStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestRustSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/subprocess/builder_test.go::TestGoNeedsStagedSDK,coding/extension/host/subprocess/builder_test.go::TestRustNeedsStagedSDK,extensions/sdk/upgrade/fixtures_test.go::TestFixturesUpgradeAndBuildand 2 more in docs/bug-ledger.json - Release note:
pig extension upgradeno longer discards the error ofContext.ConfigHomewithhome, _ := ctx.ConfigHome(). The oldConfigHomecould not fail, and an empty configuration root would make its paths relative to the working directory. A function that returns an error now returns it. In a function that does not, the upgrade lists the line for you to change. It also lists a getter passed as a function value where the old signature is wanted, instead of reporting it as an unrelated compile error. Listed lines are line numbers in your source as it was before the upgrade.
BF-9740507-3
pig extension upgrade is all or nothing
- Source: PiG's own defect
- Symptom:
pig extension upgradeis all or nothing. - Root cause:
- Move a getter out of an if or switch init statement, and move a local Context accessor (Cwd, Mode, HasUI, Width, Height, ToolCallID) that runs before it in the statement out with it, so multi-argument calls such as validateStepRuntime(ctx.Cwd(), ctx.ConfigHome(), x) are rewritten with their order of evaluation kept. - Never discard Context.ConfigHome's error: the old getter could not fail, so in a function that does not return an error the use is listed for the author instead of rewritten to
home, _ :=. Other getters keep their discard, which reproduces the old SDK's zero value on failure. - List a getter's method value passed where the old signature is wanted and classify its…
- Move a getter out of an if or switch init statement, and move a local Context accessor (Cwd, Mode, HasUI, Width, Height, ToolCallID) that runs before it in the statement out with it, so multi-argument calls such as validateStepRuntime(ctx.Cwd(), ctx.ConfigHome(), x) are rewritten with their order of evaluation kept. - Never discard Context.ConfigHome's error: the old getter could not fail, so in a function that does not return an error the use is listed for the author instead of rewritten to
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
974050757c(2026-10-12) - Tests:
coding/cli/extension_upgrade_test.go::TestUpgraderRestoresEveryFileWhenTheRebuildFails,coding/cli/extension_upgrade_test.go::TestUpgraderRestoresWrittenFilesWhenAWriteFails,extensions/sdk/upgrade/fixtures_test.go::TestFixturesUpgradeAndBuild,extensions/sdk/upgrade/rules_test.go::TestGetterMovesOutOfASwitchInit,extensions/sdk/upgrade/rules_test.go::TestListedUsesNameTheirLineBeforeTheRewrite - Release note:
pig extension upgradeis all or nothing. When writing a file or rebuilding the upgraded extension fails, it restores every file it changed and says so, instead of leaving the extension half rewritten.
BF-976af87
The documentation said PiG starts without a first-time setup screen and that Stock PiG has no built-in sprites
- Source: PiG's own defect
- Symptom: The documentation said PiG starts without a first-time setup screen and that Stock PiG has no built-in sprites.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
976af87a3d(2026-10-11) - Tests: None recorded.
- Release note: The documentation said PiG starts without a first-time setup screen and that Stock PiG has no built-in sprites. PiG shows a three-step setup (theme, sprite, anonymous usage data) on the first interactive start when no
settings.jsonexists andPIG_CODING_AGENT_DIRis not set (D88), and the Stock binary includes fifteen built-in sprites and/sprite. The Quickstart, the embedded onboarding page and the concepts page now say so, and "Your first ten minutes" shows each setup screen.
BF-97cd675
fix(agent): nested records keep a message whatever the spelling of its numbers
- Source: PiG's own defect
- Symptom: fix(agent): nested records keep a message whatever the spelling of its numbers
- Root cause: JSON.parse keeps any number, so Pi keeps a session message whatever the spelling of its numbers and writes it with JSON.stringify. made the agent's own integer members (timestamp, usage, durationMs) read any number spelling; the integer members of the records package ai decodes (diagnostics[].timestamp, deferred.expiresAt/pollAfterMs, nestedCalls.calls[].argumentsBytes/durationMs) still failed on 2.0e3 or 4.0e0, and PiG dropped the message and the branch after it.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
97cd6750b8(2026-10-10) - Tests:
agent/message_number_test.go::TestDecodedMessageKeepsNumbersInAnySpelling,agent/message_number_test.go::TestDecodedMessageOwnsItsSourceText,test/parity/scenarios/rpc/57-rpc-session-number-spelling.toml
BF-97e178f
Tool-call arguments that arrive as incomplete or malformed JSON are now recovered exactly as Pi's…
- Source: PiG's own defect
- Symptom: Tool-call arguments that arrive as incomplete or malformed JSON are now recovered exactly as Pi's partial-json parser recovers them.
- Root cause: The parser is now parseAny/parseStr/parseObj/parseArr/parseNum of partial-json with Allow.ALL and the same recovery points, JSON.parse as json.Valid plus a UseNumber decode, and the member order still recorded in schemaObjectOrder (including the replaced-key restore). The strict path falls back to the same JSON.parse reading when encoding/json refuses a valid document (1e400). A non-finite number cannot be held in a Go tool-argument object (encoding/json refuses to serialize one, Pi writes null), so it becomes null in nonFiniteToNull, the same text JSON.stringify gives. Test: TestStreamingJSONMatchesPiOnSeededInput (1850 inputs: well-formed documents cut at every boundary, corrupted with…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),e8cc072521(2026-10-08),15a5c0b670(2026-10-09),97e178f7f6(2026-10-09) - Tests:
ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,ai/trusted_url_oracle_test.go::TestTrustedVerificationURLsMatchPi,chord/delta/own_key_order_test.go::TestDiffRevisionsVisitsArrayIndexKeysFirstInNumericOrder,internal/chord/chordjson/own_key_order_test.go::TestCopyFollowsPisKeyOrderAndMessages - Release note: Tool-call arguments that arrive as incomplete or malformed JSON are now recovered exactly as Pi's partial-json parser recovers them. When a value inside an object or array is unreadable, the containers around it keep their members and read on, so text after the bad value is kept; a truncated number is cut at a lowercase
eonly (1Eis not an exponent);Infinity,-Infinity,NaNand out-of-range numbers such as1e400are read as values and becomenull(Pi serializes them asnull, and Go cannot serialize them); a__proto__member is dropped from the recovered object, as in Pi.
BF-983d651
The bash truncation notice for a last line over 50 KB that ends with a newline reports the line's size…
- Source: PiG's own defect
- Symptom: The bash truncation notice for a last line over 50 KB that ends with a newline reports the line's size instead of
line is 0B(Pi #10015). - Root cause: output-accumulator.ts getLastLineBytes returns the bytes after the final newline; the notice read 'line is 0B'. LastLineBytes now returns the open line, else the line the final newline ended.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
983d651097(2026-10-09) - Tests:
internal/codingagent/tools/last_line_bytes_test.go::TestBashLongLastLineNoticeNamesTheLineSize,internal/codingagent/tools/last_line_bytes_test.go::TestOutputAccumulatorLastLineBytes - Release note: The bash truncation notice for a last line over 50 KB that ends with a newline reports the line's size instead of
line is 0B(Pi #10015).
BF-985b5f9
fix(coding): a resumed session takes its active tools from the transcript (pi-mono #10785) [SIGNOFF-PENDING]
- Source: PiG's own defect
- Symptom: fix(coding): a resumed session takes its active tools from the transcript (pi-mono #10785) [SIGNOFF-PENDING]
- Root cause: Pi's sdk.ts always passes initialActiveToolNames, so agent-session.ts's _restoreToolsFromTranscript never runs on resume and the registration defaults replace the tools the user enabled. A resumed session (a transcript with messages) now restores them, unless the caller selected tools explicitly (an allowlist, +name/-name modifiers, NoTools). Observable difference from Pi: do not merge before owner sign-off.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
985b5f9242(2026-10-10) - Tests:
coding/cli/rpc_resume_tools_test.go::TestRPCResumeKeepsDynamicallyActivatedTools,coding/session_resume_tools_test.go::TestResumeRestoresTheTranscriptsActiveTools
BF-992f3cb
A before_provider_request replacement that is not an object leaves Anthropic Messages as Pi's `{ ...value…
- Source: PiG's own defect
- Symptom: A
before_provider_requestreplacement that is not an object leaves Anthropic Messages as Pi's{ ...value, stream: true }(null, a number or a boolean send{"stream":true}, an array or string its index members),"betas": nullsends noanthropic-betaheader, array-index keys lead a replacement body as in JavaScript, and a Codex WebSocket body's owntypekeeps the first position; a Codex payload hook that returnsnullno longer crashes PiG. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),e8cc072521(2026-10-08),433d232815(2026-10-09),992f3cbc69(2026-10-10),d37c76932f(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPiand 9 more in docs/bug-ledger.json - Release note: A
before_provider_requestreplacement that is not an object leaves Anthropic Messages as Pi's{ ...value, stream: true }(null, a number or a boolean send{"stream":true}, an array or string its index members),"betas": nullsends noanthropic-betaheader, array-index keys lead a replacement body as in JavaScript, and a Codex WebSocket body's owntypekeeps the first position; a Codex payload hook that returnsnullno longer crashes PiG.
BF-99a06cf
Interactive startup paints the editor and footer first and adds the startup header once the terminal reports…
- Source: PiG's own defect
- Symptom: Interactive startup paints the editor and footer first and adds the startup header once the terminal reports its colors or 100 ms pass, as Pi does.
- Root cause: PiG waited for the colors first, then mounted and painted editor and header in one frame.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
99a06cfc16(2026-10-09) - Tests:
coding/cli/interactive_startup_pty_test.go::TestInteractiveStartupPaintsTheEditorBeforeTheHeaderWhenTheTerminalIsSilent,internal/codingagent/extension_header_spacing_test.go::TestStartupHeaderStaysOutOfTheFirstPaintUntilTheColorsArrive - Release note: Interactive startup paints the editor and footer first and adds the startup header once the terminal reports its colors or 100 ms pass, as Pi does. On a terminal that never answers the color query, the first frame no longer waits for that timeout.
BF-9af407c
fix(ai): race builds read provider sockets through syscall.Read
- Source: PiG's own defect
- Symptom: fix(ai): race builds read provider sockets through syscall.Read
- Root cause: The one read(2) now lives in readSocketOnce: the raw system call in ordinary builds (socket_read_raw_linux.go), syscall.Read in race builds (socket_read_race_linux.go). The EINTR/EAGAIN loop, the error mapping and every result are unchanged. The monitor-wakeup test moves to a !race file because the race build's read enters a blocking system call by design.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
9af407c10f(2026-10-12) - Tests:
ai/api_streams_lazy_upstream_test.go::TestLazyAPIEntryPointsRouteToTheirOwnAPIUpstream,ai/fetch_function_openrouter_accessor_test.go::TestOpenRouterImagesAPIForwardsTheKeyedRequestToTheServer,ai/socket_read_linux_test.go::TestSocketReaderMatchesConnRead,ai/socket_read_linux_test.go::TestSocketReaderOrdersThePeersEarlierWrites,ai/socket_read_monitor_linux_norace_test.go::TestSocketWriterHelper,ai/socket_read_monitor_linux_norace_test.go::TestStreamingTransportReadDoesNotWakeTheRuntimeMonitor,ai/tool_choice_codex_upstream_test.go::TestCodexToolChoiceDefaultsToAutoAndForwardsTheOption
BF-9b4312b
A Rust extension build that ends without a compiler diagnostic (cargo or rustc killed, cargo unable to start)…
- Source: PiG's own defect
- Symptom: A Rust extension build that ends without a compiler diagnostic (cargo or rustc killed, cargo unable to start) now says how it ended: the exit status or signal, the crate whose rustc was killed, how long cargo ran and whether the build's context had ended, instead of only "cargo build failed".
- Root cause: A cargo build that was killed or failed to start was reported as "cargo build failed" with the cause only in a log that the owning test deleted. The summary now carries the exit status or signal, the process cargo says did not exit successfully (for example rustc for a crate, with its signal), how long cargo ran and whether the build's context had ended. The log records the same.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),9b4312b71e(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/extension/host/runtimecell/build_failure_report_test.go::TestBuildFailureWithoutWritableLogKeepsTheSummary,coding/extension/host/runtimecell/build_failure_report_test.go::TestRustBuildFailureNamesAKilledBuildAndHowLongItRan,coding/extension/host/runtimecell/build_failure_report_test.go::TestRustBuildFailureSummarizesRustcDiagnostic,coding/extension/host/runtimecell/rust_packed_test.go::TestBuildRustPackedCellReportsHowAKilledCargoEndedand 4 more in docs/bug-ledger.json - Release note: A Rust extension build that ends without a compiler diagnostic (cargo or rustc killed, cargo unable to start) now says how it ended: the exit status or signal, the crate whose rustc was killed, how long cargo ran and whether the build's context had ended, instead of only "cargo build failed".
BF-9b4312b-2
The Rust build failure test asserts that a killed rustc is named by its program name, not its full path
- Source: PiG's own defect
- Symptom: The Rust build failure test asserts that a killed rustc is named by its program name, not its full path.
- Root cause: A cargo build that was killed or failed to start was reported as "cargo build failed" with the cause only in a log that the owning test deleted. The summary now carries the exit status or signal, the process cargo says did not exit successfully (for example rustc for a crate, with its signal), how long cargo ran and whether the build's context had ended. The log records the same.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
9b4312b71e(2026-10-11) - Tests:
coding/extension/host/runtimecell/build_failure_report_test.go::TestBuildFailureWithoutWritableLogKeepsTheSummary,coding/extension/host/runtimecell/build_failure_report_test.go::TestRustBuildFailureNamesAKilledBuildAndHowLongItRan,coding/extension/host/runtimecell/build_failure_report_test.go::TestRustBuildFailureSummarizesRustcDiagnostic,coding/extension/host/runtimecell/rust_packed_test.go::TestBuildRustPackedCellReportsHowAKilledCargoEnded - Release note: The Rust build failure test asserts that a killed rustc is named by its program name, not its full path.
BF-9b63b2b
On Windows, the bash tool no longer risks waiting forever after a command exits while a process it started in…
- Source: PiG's own defect
- Symptom: On Windows, the bash tool no longer risks waiting forever after a command exits while a process it started in the background still holds the output pipe.
- Root cause: The bash tool on Windows puts the shell in a job object. When the shell exits,
processGroupMayHoldOutputasks Windows whether the job still has processes (QueryInformationJobObject,JobObjectBasicAccountingInformation). If the answer is zero,childwait.waitIdlewaits for the output pipe to reach EOF with no further bound, because an empty job means no process can still hold the pipe. - User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
2e0f88f794(2026-10-06, #160),65b7c13e09(2026-10-06, #168),a4753ec61e(2026-10-06, #170),ba9c37b247(2026-10-06, #171),9b63b2bc4e(2026-10-07, #176),ce8587ef40(2026-10-07, #179) - Tests:
coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,coding/cli/rpc_shutdown_test.go::TestRPCInputEndAfterExtensionCommandComparedWithPi,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleBusyEntriesAreSkippedNotErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledBeforeStartTouchesNothing,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledDuringTheLastEntryReportsTheCancellation,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleEntryLeasedAfterClassificationIsSkippedNotAnError,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleOtherErrorsStayErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleStopsWhenItsContextIsCancelledand 88 more in docs/bug-ledger.json - Release note: On Windows, the bash tool no longer risks waiting forever after a command exits while a process it started in the background still holds the output pipe. The check that asks Windows whether the command's job still has processes could read an empty answer when Go moved the goroutine stack during the call, and the tool then waited for the pipe to close. The buffers these job-object calls pass to Windows now stay in place.
BF-9b63b2b-2
A running ! command or bash tool command no longer outlives pig when the session ends because its…
- Source: PiG's own defect
- Symptom: A running
!command or bash tool command no longer outlivespigwhen the session ends because its terminal closed orpigcrashed. - Root cause: The bash tool on Windows puts the shell in a job object. When the shell exits,
processGroupMayHoldOutputasks Windows whether the job still has processes (QueryInformationJobObject,JobObjectBasicAccountingInformation). If the answer is zero,childwait.waitIdlewaits for the output pipe to reach EOF with no further bound, because an empty job means no process can still hold the pipe. - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a4753ec61e(2026-10-06, #170),9b63b2bc4e(2026-10-07, #176) - Tests:
coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,coding/extension/host/runtimecell/hash_inputs_test.go::TestCommandVersionCachesWarmProbeAndDetectsReplacement,coding/extension/host/subprocess/node_recovery_test.go::TestNodeUnattributableCrashRestartsWholeGroup,internal/codingagent/exit_record_test.go::TestBackgroundGroupPanicReachesTheHandlerWithItsStack,internal/codingagent/exit_record_test.go::TestBackgroundTaskPanicEndsTheSessionWithACrashRecord,internal/codingagent/exit_record_test.go::TestCacheWarmerDecisionPanicKeepsPisDecision,internal/codingagent/exit_record_test.go::TestCacheWarmerRefreshPanicOutsideTheCaughtRequestReachesTheUncaughtHandler,internal/codingagent/exit_record_test.go::TestCacheWarmerRefreshRequestPanicIsCaughtAndReschedulesand 16 more in docs/bug-ledger.json - Release note: A running
!command or bash tool command no longer outlivespigwhen the session ends because its terminal closed orpigcrashed. The command runs in its own process group, so the terminal's hangup never reached it, andpignow kills it before it exits, as Pi does.
BF-9ba2589
fix(agent): write a decoded message as the object it was decoded from
- Source: PiG's own defect
- Symptom: fix(agent): write a decoded message as the object it was decoded from
- Root cause: Pi keeps the object JSON.parse returned for a session line or an extension result and every writer stringifies it, so a message whose members are in another order, or which holds members PiG does not model, comes back unchanged from RPC get_messages, extension events and buildSessionContext. PiG re-encoded the decoded fields in its own order.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
9ba2589119(2026-10-10) - Tests:
agent/message_source_test.go::TestDecodedMessageInPiOrderRetainsNoText,agent/message_source_test.go::TestDecodedMessageWritesTheObjectItWasDecodedFrom,agent/message_source_test.go::TestWithEmptyContentSpreadsContent,agent/tool_result_fields_test.go::TestToolResultMessageFieldsMatchUpstream
BF-9be5765
Pressing Escape while an extension tool runs now lets the tool finish: a tool that stops on its abort signal…
- Source: A Pi defect that PiG fixed first (divergence D111)
- Symptom: Every extension tool call failed at once with "context canceled".
- Root cause: On abort, the host cancelled the tool_call request and returned "context canceled" at once, so even a tool that honours its AbortSignal lost its own result. Pi's executePreparedToolCall awaits tool.execute and keeps its result or thrown message (agent-loop.ts:831-853); a tool that ignores the signal holds Pi's run forever.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: none recorded
- Removed when Pi fixes it: Pi stops awaiting aborted tool calls.
- Fix commits:
101fe6be1c(2026-10-09),2bdd5278de(2026-10-09),6b51bbc36c(2026-10-09),9be576533f(2026-10-09) - Tests:
coding/cli/rpc_tool_abort_grace_test.go::TestRPCAbortedExtensionToolSettlesAndFreesTheSession,coding/extension/host/subprocess/liveness_test.go::TestCancelledToolCallWaitsTheGraceForItsAnswer,coding/extension/host/subprocess/liveness_test.go::TestRequestCancellationWinsOverLateResponse,coding/extension/host/subprocess/tool_abort_grace_test.go::TestAbortedToolCallSettlesCooperativelyOrFailsAfterTheGrace,test/extension-conformance/tool_abort_grace_test.go::TestConformance_AbortedToolSettlesWithinTheGrace - Release note: Pressing Escape while an extension tool runs now lets the tool finish: a tool that stops on its abort signal reports its own result or error, as in Pi. A tool that ignores the signal fails with "Operation aborted" after 3 seconds, and the session becomes usable again; Pi waits for such a tool forever (D111). Before, every extension tool call failed at once with "context canceled".
BF-9c1a0e9
An OpenAI Responses stream event or output item with a member of an unexpected type is read as Pi's…
- Source: PiG's own defect
- Symptom: An OpenAI Responses stream event or output item with a member of an unexpected type is read as Pi's JavaScript reads it instead of ending the turn with
openai-responses: invalid SSE JSONor silently skipping the item: a delta appends its string form (nulland a missing delta appendnullandundefined), a finished message or reasoning item joins its parts' string forms and ends the turn withitem.content?.map is not a function(oritem.summary?...) when they are not an array, a function call's id interpolates a non-stringcall_id, non-string function-call arguments end the turn with Pi'spartialJson.trim is not a functionorstartsWitherror, a usage that is not an object reads as zeros, a status that is not a string ends the turn withUnhandled stop reason: <its string form>, and a failed response's non-string error code and message are written in their string form. - Root cause: google-generative-ai.ts:236-237 reads
output: (usageMetadata.candidatesTokenCount || 0) + (usageMetadata.thoughtsTokenCount || 0), sotrueadds 1 and the usage stays numeric. PiG's JS read of a mistyped chunk refused a truthy non-number count and ended the turn with the decode error. normalizeGeminiUsage now reads atruecandidates count as 1; every other count is stored as read (a truthy non-number thoughts, total or cached count still ends the turn, because Pi stores it raw). The prompt count is only subtracted from and already counted as its Number(). - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),366969bbae(2026-10-11),5078f73100(2026-10-11),5932542c21(2026-10-11),71e9d13eb9(2026-10-11),82414338e7(2026-10-11),8eb1202528(2026-10-11),9c1a0e938d(2026-10-11),b6afa7be1a(2026-10-11),db9a761dd8(2026-10-11),e098463b19(2026-10-11),e1abc6c615(2026-10-11),f1a59f9161(2026-10-11) - Tests:
ai/anthropic_event_member_type_oracle_test.go::TestAnthropicEventMistypedMembersMatchPi,ai/google_chunk_member_type_oracle_test.go::TestGoogleChunkMistypedMembersMatchPi,ai/mistral_chunk_member_type_oracle_test.go::TestMistralChunkMistypedMembersMatchPi,ai/openai_completions_member_type_oracle_test.go::TestOpenAICompletionsMistypedMembersMatchPi,ai/openai_responses_member_type_oracle_test.go::TestOpenAIResponsesItemPiStoresRawEndsTheTurnWithTheDecodeError,ai/openai_responses_member_type_oracle_test.go::TestOpenAIResponsesMistypedMembersMatchPi,ai/openai_responses_response_oracle_test.go::TestProcessResponsesStreamEdgeEventsMatchPi,ai/utils_exports_upstream_test.go::TestTextSignatureV1WireShapeAndParsingand 7 more in docs/bug-ledger.json - Release note: An OpenAI Responses stream event or output item with a member of an unexpected type is read as Pi's JavaScript reads it instead of ending the turn with
openai-responses: invalid SSE JSONor silently skipping the item: a delta appends its string form (nulland a missing delta appendnullandundefined), a finished message or reasoning item joins its parts' string forms and ends the turn withitem.content?.map is not a function(oritem.summary?...) when they are not an array, a function call's id interpolates a non-stringcall_id, non-string function-call arguments end the turn with Pi'spartialJson.trim is not a functionorstartsWitherror, a usage that is not an object reads as zeros, a status that is not a string ends the turn withUnhandled stop reason: <its string form>, and a failed response's non-string error code and message are written in their string form. OpenAI Codex streams read their events the same way. A missing ornulloutput item ends the turn with Pi'sCannot read properties of ... (reading 'type')error, a message's text signature records its id and any non-empty phase as Pi'sJSON.stringifywrites them, and an output item whose mistyped member Pi keeps as-is ends the turn with the decode error instead of being dropped from an otherwise successful answer.
BF-9c98038
An unclosed $$ or \[ display-math block no longer swallows or corrupts every Markdown table after it
- Source: A fix Pi shipped that PiG ported
- Symptom: An unclosed
$$or\[display-math block no longer swallows or corrupts every Markdown table after it. - Root cause: Port Pi (packages/tui/src/components/markdown.ts containsTableStart, TABLE_DELIMITER_ROW_REGEX, tokenizeBlockLatex, #11159): a block-math token no longer spans a GFM table and the pending-math fallback skips one; a pending [ also needs a non-empty body. The eight upstream 'unclosed display math before tables' cases are ported verbatim, with a live-Pi comparison of them and their neighbours.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
9c980381eb(2026-10-20) - Tests:
tui/markdown_latex_table_test.go::TestMarkdownContainsTableStart,tui/markdown_latex_table_test.go::TestMarkdownUnclosedDisplayMathBeforeTables,tui/markdown_latex_table_test.go::TestMarkdownUnclosedDisplayMathBeforeTablesMatchesPi - Release note: An unclosed
$$or\[display-math block no longer swallows or corrupts every Markdown table after it. A display-math block never spans a table, and the streaming pending-math fallback no longer treats a following table as pending math (Pi 2ed3118b, #11159).
BF-9d4fe9c
Selecting a model or cycling models with Ctrl+P no longer freezes the interactive mode when an extension's…
- Source: PiG's own defect
- Symptom: Selecting a model or cycling models with Ctrl+P no longer freezes the interactive mode when an extension's
model_selecthandler registers a tool. - Root cause: Pi's shutdown ends with process.exit and does not await a pending model_select (interactive-mode.ts:4251-4291). PiG's notification Emit ran on the Session's background context, so Stop's backgroundTasks.Wait joined a slow or hung handler. /quit, Ctrl+D or a signal therefore tore the UI down but did not exit.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),71d96b9841(2026-10-12),778d3b9398(2026-10-12),9d4fe9ce3d(2026-10-12),c21b118fc8(2026-10-12),f059079215(2026-10-12) - Tests:
coding/session_model_extension_order_test.go::TestBeginModelCycleOnMainDefersNotificationsToTheCompletion,coding/session_model_extension_order_test.go::TestModelChangeOnMainNotificationsEndWithTheirContext,coding/session_model_owner_test.go::TestSetModelOnMainDispatchesAllStateBeforeNotifications,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,internal/codingagent/interactive_model_input_live_test.go::TestInteractiveModelChangeKeepsInputLiveand 6 more in docs/bug-ledger.json - Release note: Selecting a model or cycling models with Ctrl+P no longer freezes the interactive mode when an extension's
model_selecthandler registers a tool. As in Pi, the mode no longer waits for the handlers, whether the change comes from Ctrl+P, the model selector or a typed/model. The new model applies at once, and keys typed while the handlers run reach the focused component: the model selector, which stays open until the change settles (Escape closes it at once), or the editor. A prompt submitted meanwhile starts at once on the new model. A second change applies its model at once, and its handlers run alongside the first change's. An extension dialog opened meanwhile closes the selector, so later keys reach the editor. Quitting while a handler still runs no longer waits for it, and a failed model switch shows the error's message, as Pi does.
BF-9d64980
fix(coding): provider order and availability of virtual models follow Pi
- Source: PiG's own defect
- Symptom: fix(coding): provider order and availability of virtual models follow Pi
- Root cause: A provider of only virtual models keeps the place of its ID in providerIds order (a loaded models.json lists it even when it composes to nothing; a failed one defines nothing), allModels and allAvailable place its models there, and the virtual models of a provider follow that provider's credentials, so an extension provider without a key no longer exposes them.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
9d64980afc(2026-10-10) - Tests: None recorded.
BF-9f4702c
A failed extension shortcut handler is now reported as Error: Shortcut handler error: … in the error…
- Source: PiG's own defect
- Symptom: Previously it was shown as a warning.
- Root cause: PiG used showWarning ("Warning: ...").
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
9f4702c4e1(2026-10-13) - Tests:
internal/codingagent/interactive_command_error_test.go::TestInteractiveShortcutDiagnosticOwnership - Release note: A failed extension shortcut handler is now reported as
Error: Shortcut handler error: …in the error color, as in Pi. Previously it was shown as a warning.
BF-D110
Git sources: #subdirectory= selects a subdirectory; git:// and upper-case schemes yield usable…
- Source: A Pi defect that PiG fixed first (divergence D110)
- Symptom: for (2) and (3) Pi's
parseGitUrlreturns a repository no Git client can clone (https:////host/org/repo,https://HTTPS://host/org/repo), sopi installof such a source fails at clone time. - Root cause: (1) is additive:
@<commit>#subdirectory=<path>is how PiG installs a Piglet that lives in a repository subdirectory, and no Pi input is rejected for it. (2) and (3) are Pi results that no Git client can clone; PiG returns the repository the user wrote. - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: none recorded
- Removed when Pi fixes it: Pi accepts a
#subdirectory=spelling or PiG stops installing Piglets from repository subdirectories (1); Pi fixes thegit://and upper-case scheme repositories (2, 3) or parsesgit:file://sources (4). - Fix commits:
478157bb22(2026-10-09),9be576533f(2026-10-09),eda54e987b(2026-10-09) - Tests:
coding/source/parse_git_oracle_test.go::TestParseGitMatchesPi,coding/source/ref_test.go::TestParseGitFragmentIsARefUnlessItIsSubdirectory,coding/source/ref_test.go::TestParseGitSubdirectory,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode
BF-D150
--no-stdin skips the startup read of piped stdin (Pi #10415)
- Source: A Pi defect that PiG fixed first (divergence D150)
- Symptom:
pig --no-stdin -p "prompt"with an open stdin pipe runs the prompt; Pi hangs. Pi parses--no-stdinas an unknown extension flag. - Root cause:
pi -p hi < <(sleep 15)never starts:readPipedStdin(packages/coding-agent/src/main.ts:80-97) waits forendon a pipe whose writer stays open, and the same happens in PiG. An idle open pipe cannot be told from a slow producer, so a timeout would drop real input; an explicit opt-out is the only fix that cannot lose piped input. Callers that keep an input pipe open (agent harnesses, CI wrappers) pass the flag. Pi has no fix: Pi #10415 has an unmerged--no-stdinproposal and no PR. Pi #5571 discusses the same hang. - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: https://github.com/earendil-works/pi/issues/10415
- Removed when Pi fixes it: Pi accepts
--no-stdinor otherwise lets a caller start with an open stdin pipe, or fixes #10415. - Fix commits:
644b3e0a2a(2026-10-09),0225a43aa5(2026-10-10) - Tests:
coding/cli/no_stdin_test.go::TestNoStdinStartsWithAnOpenInputPipe,coding/cli/no_stdin_test.go::TestParseArgsNoStdin
BF-D151
grep with context reads a window of each matched file (Pi #9276)
- Source: A Pi defect that PiG fixed first (divergence D151)
- Symptom: memory use of a
grepcall follows the context size, not the file size. A customGrepOperations.ReadFilestill returns the whole text, but only the current file is kept. A customGrepOperationswithoutReadFilestreams the local file. - Root cause: with
context: 2over a directory of 100 MB logs, Pi holds each matched file several times over (read string, split array) and exhausts the heap (Pi #9276). Pi has no fix. The output is the same: the window splits lines asgetFileLinesdoes, keeps the firstGrepMaxLineLength + 1code points of a line (the rest cannot reach the output), and still prints overlapping context blocks for nearby matches. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: https://github.com/earendil-works/pi/issues/9276
- Removed when Pi fixes it: Pi stops loading whole files for grep context.
- Fix commits:
2a78d0efdf(2026-10-09),644b3e0a2a(2026-10-09),e98c8af606(2026-10-09) - Tests:
internal/codingagent/tools/grep_context_memory_test.go::TestGrepContextDoesNotLoadTheMatchedFile,internal/codingagent/tools/grep_context_memory_test.go::TestGrepLineWindowMatchesTheWholeFileSplit
BF-D153
The bash truncation notice names the size of the last line, not the bytes after the final newline (Pi #10015)
- Source: A Pi defect that PiG fixed first (divergence D153)
- Symptom: PiG prints
line is 58.6KB; Pi printsline is 0B. Output without a trailing newline is the same in both. - Root cause: The tail it shows belongs to line 1, which is 58.6KB. Pi has no fix (Pi #10015 is open, with no PR).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: https://github.com/earendil-works/pi/issues/10015
- Removed when Pi fixes it: Pi reports the size of the last counted line.
- Fix commits:
983d651097(2026-10-09) - Tests:
internal/codingagent/tools/last_line_bytes_test.go::TestBashLongLastLineNoticeNamesTheLineSize,internal/codingagent/tools/last_line_bytes_test.go::TestOutputAccumulatorLastLineBytes
BF-D155
Selection lists page with PageUp and PageDown (Pi #7629)
- Source: A Pi defect that PiG fixed first (divergence D155)
- Symptom: in a selector, PageDown moves the selection down one page and does not scroll the transcript behind it. Pi scrolls the transcript or does nothing. An open editor autocomplete list pages the same way, and a user binding of the same key to an app action does not fire while the list is open. Extension-provided overlays cannot yet report that they capture the page keys, so the viewport still takes them there.
- Root cause: the selection stays where the user is looking. Pi #7629 is open, and its PR #7680 (
packages/tui/src/tui-alt-screen.ts,getPageSelectionIndex,capturesSelectPageInput) is unmerged. - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: https://github.com/earendil-works/pi/issues/7629
- Removed when Pi fixes it: Pi merges PR #7680 or another change that pages selection lists on these keys; then restore the keys in the oracle generators.
- Fix commits:
68a4ed2b54(2026-10-09),0d867c440e(2026-10-10) - Tests:
internal/codingagent/fullscreen_modal_viewport_test.go::TestFullscreenSelectionKeepsItsPageKeys,internal/codingagent/llama/ui_test.go::TestHuggingFaceSearchPageKeysMoveTenResults,internal/codingagent/modal_viewport_input_test.go::TestEditorSlotSelectorPageKeyMovesSelectionInEveryMode,tui/select_page_keys_test.go::TestAltScreenGivesSelectionPageKeysToACapturingFocus,tui/select_page_keys_test.go::TestEditorAutocompletePageKeys,tui/select_page_keys_test.go::TestPageSelectionIndex,tui/select_page_keys_test.go::TestSelectListPageKeysMoveByAPage,tui/select_page_keys_test.go::TestSelectorComponentsMoveByTheirPageand 1 more in docs/bug-ledger.json
BF-D156
MCP server url expands environment references (Pi #10654)
- Source: A Pi defect that PiG fixed first (divergence D156)
- Symptom: the
/mcpserver menu adds aconnects to: <expanded url>line under aurlthat expands to something else (owner-approved; the expansion can contain a secret from the environment, and the menu shows it). An entry whoseurlis only a reference loads in PiG and is rejected by Pi. An entry whoseurlcontains$$, or$NAMEor${NAME}withNAMEset, connects to the expanded URL in PiG and to the literal text in Pi. The stored config andgetMcpServerskeep the URL as written, and the server list shows it as written. - Root cause: The reporter asks for the URL to be resolved with the same logic as headers and env values. Pi #10654 is open with no PR.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: https://github.com/earendil-works/pi/issues/10654
- Removed when Pi fixes it: Pi expands the
urlof an MCP server. - Fix commits:
b06ad928d7(2026-10-09),0c8e8b07be(2026-10-10),922bd78903(2026-10-10) - Tests:
coding/extension/mcp_url_expansion_test.go::TestExpandMcpURL,coding/extension/mcp_url_expansion_test.go::TestValidateMcpServerConfigChecksTheExpandedURL,coding/mcpext/connection_test.go::TestMCPConnectionsExpandTheServerURLReferences,coding/mcpext/manager_test.go::TestMcpManagerServerMenuShowsTheExpandedURL
BF-D195
An empty AWS_BEARER_TOKEN_BEDROCK is no token, where Pi's AWS SDK treats the variable as set and the request…
- Source: A Pi defect that PiG fixed first (divergence D195)
- Symptom: the failure is an AWS JavaScript SDK side effect that Pi's own
getProviderEnvValuedoes not intend. Pi decides "no token" and the SDK decides "bearer". - Root cause: an empty variable names no credential. Failing every request because an unset secret was exported as an empty string breaks a working IAM or profile setup for no reason a user can act on, and PiG's own lookup, like Pi's, already treats the value as absent. The Go SDK PiG uses agrees:
resolveEnvBearerTokenreturns on an empty value (third_party/awssdk/service/bedrockruntime/api_client.go:980-984). - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: none recorded
- Removed when Pi fixes it: Pi or its AWS SDK treats an empty
AWS_BEARER_TOKEN_BEDROCKas unset (Pi passesauthSchemePreferenceitself, or the SDK tests for a non-empty value), or an owner decision reverses this one; then the marker, the D195 cases of the test and this record go. - Fix commits:
cca788e454(2026-10-24) - Tests:
ai/bedrock_bearer_divergence_test.go::TestBedrockBearerTokenDivergencesFromPi,ai/bedrock_request_body_oracle_test.go::TestBedrockRequestBodyMatchesPi
BF-D196
Under skip-auth a bearer token from the process environment is not sent over an http:// endpoint, where Pi's…
- Source: A Pi defect that PiG fixed first (divergence D196)
- Symptom: Pi sends a credential over plain HTTP because the JavaScript SDK has no HTTPS check for bearer tokens, and its skip-auth option does not stop the SDK from picking up the token.
- Root cause: the refusal keeps a bearer token off an unencrypted connection. Pi's behavior sends it in clear text to a host the user chose; PiG does not override the Go SDK's guard to repeat that.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: none recorded
- Removed when Pi fixes it: Pi refuses to send a bearer token over HTTP (its SDK adds an HTTPS check, or Pi installs a signer that does), or PiG installs
bedrockBearerSignerunder skip-auth and the owner approves sending the token over HTTP there; then the markers, the D196 case of the test and this record go. - Fix commits:
cca788e454(2026-10-24) - Tests:
ai/bedrock_bearer_divergence_test.go::TestBedrockBearerTokenDivergencesFromPi
BF-D198
The Go, Rust and Python Type has no Codec family (Codec, Decode, Encode, DecodeBuilder…
- Source: A Pi defect that PiG fixed first (divergence D198)
- Symptom: none.
- Root cause: a Codec's callbacks have no consumer outside
Value.DecodeandValue.Encode(typebox/build/value/codec/callback.mjs, the only reader of~codec), which pi-ai does not export (ai/src/index.tsexportsStatic,TSchemaandTypefrom typebox) and no SDK serves. Pi's own tool-argument validation does not call them (ai/src/utils/validation.tsusesValue.ConvertandCompile(schema).Check). A port could store the callbacks, but nothing in any SDK or in the host could ever call them, which is an inert member that accepts the call and does nothing. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: none recorded
- Removed when Pi fixes it: an SDK serves
Value.Decode/Value.Encode(or a host call runs a codec) so a stored callback pair has a consumer. - Fix commits:
a8c3cdfb85(2026-10-24),eb9071495b(2026-10-25) - Tests:
extensions/sdk/type_vectors_test.go::TestTypeOmitsTheCodecFamily,test/extension-conformance/type_schema_registration_test.go::TestConformance_TypeSchemaToolParametersAgreeAcrossSDKs
BF-D200
With no home directory PiG still runs the commands that load no resources, where Pi crashes at import, and…
- Source: A Pi defect that PiG fixed first (divergence D200)
- Symptom: none claimed; Pi's behaviour is a crash at load rather than a reported error, and is kept for the paths that need a home directory.
- Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: none recorded
- Removed when Pi fixes it: Pi stops resolving the home directory at import (so
--versionandlistrun there without one), or an owner decision reverses this one; then PiG's startup fails at the same point as Pi's, the markers, the allowance test and this record go. - Fix commits:
09c5680c80(2026-10-24),99f0027ef1(2026-10-24) - Tests:
coding/cli/no_home_directory_test.go::TestNoHomeDirectoryPiCrashesAtImportWherePiGRunsResourceFreeCommands,coding/cli/no_home_directory_test.go::TestStartupFailsWhereNoHomeDirectoryExists,coding/cli/no_home_directory_test.go::TestStartupFollowsNodeWhenThereIsNoHomeDirectory
BF-D203
registerProvider with an empty apiKey leaves the provider unconfigured where Pi treats the empty string as…
- Source: A Pi defect that PiG fixed first (divergence D203)
- Symptom: the inconsistency above:
""is a configured key toconfiguredApiKey,configuredRequestAuthStatusand the composedcheck, and a missing one toprovider-composer.ts:318. - Root cause: Pi's behaviour is an accident of
??on a string. The empty string is not a form the option documents (packages/coding-agent/src/core/extensions/types.ts:1936: "API key literal, env interpolation ($ENV_VAR or ${ENV_VAR}), or leading !command"), and Pi treats""as missing where it validates the same option (provider-composer.ts:318,!config.apiKey). Its effect is a provider that reads configured and available whose every request fails withNo API key for provider: <id>(for examplepackages/ai/src/api/openai-completions.ts:95), so an extension that passesprocess.env.KEY ?? ""gets a selectable provider that cannot answer. Distinguishing""from an absent key needs presence… - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Pi issue: none recorded
- Removed when Pi fixes it: Pi treats
""as missing inconfiguredApiKey, or the SDK API gains explicit presence forapiKey(a pointer or an option) and an owner decision asks for Pi's behaviour; then the three markers, the test and this record go. - Fix commits:
90ffbef9d1(2026-10-25) - Tests:
coding/register_provider_empty_apikey_test.go::TestRegisterProviderWithEmptyAPIKeyIsNotConfigured
BF-a076a50
A model request on a reused connection gets the full HTTP idle timeout to receive its first response bytes…
- Source: PiG's own defect
- Symptom: The time the connection had spent idle in the pool was deducted from the next request's timeout.
- Root cause: With a 3 s idle timeout, a POST on a connection idle for 2 s whose headers take 2 s failed with "fetch failed", 3 of 3. A GET hid it, because net/http replays an idempotent request on a fresh connection. A model POST whose body was written is not replayed.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
a076a50576(2026-10-10) - Tests:
ai/http_transport_reuse_test.go::TestReusedStreamingConnectionGivesTheNextRequestTheFullIdleTimeout - Release note: A model request on a reused connection gets the full HTTP idle timeout to receive its first response bytes, as in Pi. Before, the time the connection had spent idle in the pool was deducted from the next request's timeout.
BF-a09da26
pi.getSettings() in a subprocess extension lists the settings in Pi's order: the global settings file's…
- Source: PiG's own defect
- Symptom:
pi.getSettings()in a subprocess extension lists the settings in Pi's order: the global settings file's members as written (after the legacy-key migration), then members only the project file sets, with nested objects merged the same way and setters and overrides adding members after the others. - Root cause: settings.go (mapkeyorder): pi.getSettings() is structuredClone(this.settings), deepMergeSettings of the migrated global and project file objects (settings-manager.ts:195-212, 566-568), so a Node extension sees the files' member order. The state push sorted them (json.Marshal of a map). The manager now keeps each layer's object as Pi holds it (the migrated file object, patched in place by setters as persistScopedSettings patches the file), merges them with deepMergeObjects semantics, and encodes the extension settings in that order (orderedjson.MarshalInSourceOrder). Interactive mode binds getSettingsJSON too. The typed-Settings-to-object decode stays: allowed as the conversion mechanic…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a09da26616(2026-10-13) - Tests:
coding/session_settings_json_test.go::TestSessionSettingsJSONIsTheEncodingOfGetSettings,internal/codingagent/settings_extension_json_test.go::TestExtensionSettingsJSONEncodesOncePerSettingsGeneration,internal/codingagent/settings_extension_json_test.go::TestExtensionSettingsJSONKeepsPiMemberOrder,internal/codingagent/settings_shared_read_test.go::TestSettingsReadHonorsPiWriterLock,internal/codingagent/tool_render_shell_details_test.go::TestShellCardIgnoresACaseVariantTruncatedBy,internal/codingagent/tool_render_shell_details_test.go::TestShellDetailsFromReadsTheExactKeys - Release note:
pi.getSettings()in a subprocess extension lists the settings in Pi's order: the global settings file's members as written (after the legacy-key migration), then members only the project file sets, with nested objects merged the same way and setters and overrides adding members after the others. PiG sorted them alphabetically.
BF-a09da26-2
A bash or PowerShell tool card reads the result's truncation details by their exact keys, as Pi does: a…
- Source: PiG's own defect
- Symptom: A bash or PowerShell tool card reads the result's
truncationdetails by their exact keys, as Pi does: a member such asTRUNCATEDBYno longer stands in fortruncatedBy. - Root cause: settings.go (mapkeyorder): pi.getSettings() is structuredClone(this.settings), deepMergeSettings of the migrated global and project file objects (settings-manager.ts:195-212, 566-568), so a Node extension sees the files' member order. The state push sorted them (json.Marshal of a map). The manager now keeps each layer's object as Pi holds it (the migrated file object, patched in place by setters as persistScopedSettings patches the file), merges them with deepMergeObjects semantics, and encodes the extension settings in that order (orderedjson.MarshalInSourceOrder). Interactive mode binds getSettingsJSON too. The typed-Settings-to-object decode stays: allowed as the conversion mechanic…
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
a09da26616(2026-10-13) - Tests:
coding/session_settings_json_test.go::TestSessionSettingsJSONIsTheEncodingOfGetSettings,internal/codingagent/settings_extension_json_test.go::TestExtensionSettingsJSONEncodesOncePerSettingsGeneration,internal/codingagent/settings_extension_json_test.go::TestExtensionSettingsJSONKeepsPiMemberOrder,internal/codingagent/settings_shared_read_test.go::TestSettingsReadHonorsPiWriterLock,internal/codingagent/tool_render_shell_details_test.go::TestShellCardIgnoresACaseVariantTruncatedBy,internal/codingagent/tool_render_shell_details_test.go::TestShellDetailsFromReadsTheExactKeys - Release note: A bash or PowerShell tool card reads the result's
truncationdetails by their exact keys, as Pi does: a member such asTRUNCATEDBYno longer stands in fortruncatedBy.
BF-a110f68
fix(tui,codingagent): owner-loop frame deadline and reader-side input routing (TU-T11, TU-T12)
- Source: PiG's own defect
- Symptom: fix(tui,codingagent): owner-loop frame deadline and reader-side input routing (TU-T11, TU-T12)
- Root cause: Throttled frames keep their deadline on one reusable owner-loop timer (SetRenderDeadline/RunDueRender) instead of an AfterFunc goroutine per frame. The terminal reader frames input and routes it to the owner itself; the last chunk of a read goes out without waiting for its ticket, which settles into one per-router channel, and the read boundary travels on the chunk (more) instead of a readDone channel.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
a110f6800b(2026-10-13) - Tests: None recorded.
BF-a13bae4
A read result shown in the transcript no longer keeps the file text a second time as its structured content
- Source: PiG's own defect
- Symptom: A
readresult shown in the transcript no longer keeps the file text a second time as its structured content. - Root cause: A finished tool card keeps its result for its renderers, and its body renderer closure keeps it too, for as long as the transcript shows the card. A read result's structured content is the text again as a JSON string (toReadOutput, read.ts), so every read card held its text twice, once escaped. In Pi structuredContent is the same string as the text, so the card holds it once.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
06c408c21d(2026-10-11),8ab5a89874(2026-10-11),a13bae4664(2026-10-11),eab3caf769(2026-10-11) - Tests:
internal/codingagent/session_store_test.go::TestAppendedMessageIsHeldOnceWhenItsRecordDecodesToIt,internal/codingagent/session_store_test.go::TestAppendedToolResultWithTypedDetailsIsHeldOnce,internal/codingagent/tool_card_structured_test.go::TestReadCardKeepsTheTextOnceAndRenderersGetStructuredContent,internal/codingagent/tool_card_structured_test.go::TestToolCardKeepsStructuredContentItCannotDerive,internal/codingagent/tools/truncation_share_test.go::TestTruncatedResultDetailsShareTheReturnedText - Release note: A
readresult shown in the transcript no longer keeps the file text a second time as its structured content. Renderers, including extension renderers, still receive the structured content the tool returned. In a 75-prompt session of file reads (about 4.2M tokens of context), memory at the end drops by another 20 MB or so.
BF-a156ef3
On Linux, clipboard reads accept every DISPLAY spelling Pi's xcb_connect accepts: white space or a sign…
- Source: PiG's own defect
- Symptom: Previously these spellings failed, and image and text paste reported the clipboard as unavailable.
- Root cause: Pi opens the clipboard with xcb_connect (linux-platform-x11.c:185), whose parser reads the display and screen with C strtoul into an int (libxcb 1.15 xcb_util.c:176, :186): leading white space and a sign are accepted, a minus negates modulo 2^64, an overflow saturates, the int keeps the low 32 bits, and a network display's port is the low 16 bits of 6000 plus the number (:244). Pi then steps past screen_number screens (linux-platform-x11.c:188), so a negative screen selects the first. PiG used strconv.Atoi and rejected negative numbers and TCP displays above 59535, so DISPLAY=": 1", ":1.-1" or "host:65536+n" failed where Pi connects.
- User impact: An operation failed or was rejected
- Platform: Linux
- Fixed in: 0.5.0
- Fix commits:
74b56b5573(2026-10-13),a156ef3cfb(2026-10-13) - Tests:
internal/nativeplatform/clipboard_x11_display_linux_test.go::TestX11DisplayTransportPrefixes,internal/nativeplatform/clipboard_x11_display_range_linux_test.go::TestX11DisplayNumbersParseAsXcbDoes,internal/nativeplatform/clipboard_x11_display_range_linux_test.go::TestX11DisplaySpellingsConnectAsPiDoes - Release note: On Linux, clipboard reads accept every
DISPLAYspelling Pi'sxcb_connectaccepts: white space or a sign before the display or screen number (: 1,:+1), a negative screen, which selects the first screen, and display numbers that wrap as libxcb'sstrtouland 16-bit port do (host:65536reaches port 6000). Previously these spellings failed, and image and text paste reported the clipboard as unavailable.
BF-a178939
The Go and Python serializeConversation keep an unpaired UTF-16 surrogate in a message, a tool-call name or…
- Source: PiG's own defect
- Symptom: The Go and Python
serializeConversationkeep an unpaired UTF-16 surrogate in a message, a tool-call name or argument, and when the 2,000-unit cut splits a surrogate pair, as Pi does; the conformance test now compares those cases against Pi's own code. - Root cause: A 6,000-message differential against Pi's own serializeConversation (node + dist) now has 0 differences in Go and Python. Before the fix it had 53 in Go and 33 in Python. The conformance row cannot see this, because the harness decodes reports with encoding/json, which turns every lone surrogate into U+FFFD.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
058e7a0e4b(2026-10-11),15cb4f2db8(2026-10-11),19966b4089(2026-10-11),50f526d69e(2026-10-11),a178939d6a(2026-10-11),a4e5b071cb(2026-10-11),e4aa58a078(2026-10-11),e8473d13c5(2026-10-11),f51d8b4bea(2026-10-11) - Tests:
extensions/sdk-py/tests/test_compaction_estimate.py,extensions/sdk-py/tests/test_serialize_conversation_surrogates.py,extensions/sdk/compaction_estimate_test.go::TestEstimateTokensCountsUTF16UnitsAndJSONStringifyLength,extensions/sdk/compaction_estimate_test.go::TestJSNumberStringMatchesNumberToString,extensions/sdk/serialize_conversation_surrogate_test.go::TestSerializeConversationKeepsUnpairedSurrogates,test/extension-conformance/compaction_usage_test.go::TestConformance_CompactionUsageHelpersAgreeAcrossSDKs,test/extension-conformance/host_parser_helpers_test.go::TestConformance_HostParserHelpersAgreeAcrossSDKs,test/extension-conformance/message_conversion_test.go::TestConformance_MessageConversionAgreesAcrossSDKsand 2 more in docs/bug-ledger.json - Release note: The Go and Python
serializeConversationkeep an unpaired UTF-16 surrogate in a message, a tool-call name or argument, and when the 2,000-unit cut splits a surrogate pair, as Pi does; the conformance test now compares those cases against Pi's own code.
BF-a1fa6d1
NO_PROXY entries and ports are trimmed with JavaScript's whitespace rules, as in Pi: an entry or port that…
- Source: PiG's own defect
- Symptom: NO_PROXY entries and ports are trimmed with JavaScript's whitespace rules, as in Pi: an entry or port that starts with U+0085 is no longer stripped, and one that starts with U+FEFF is.
- Root cause: It went red on U+0085: Go's TrimSpace stripped a leading NEL from a NO_PROXY entry (parseNoProxyEntry, node-http-proxy.ts:42 entry.trim()) and from a port (Number.parseInt), where JavaScript does not, so a proxied target was excluded. parseNoProxyEntry uses jsstring.Trim then jsstring.ToLower in Pi's order, proxyPort jsstring.TrimStart (parseInt ignores trailing text), and the two NO_PROXY lowercase calls use jsstring.ToLower. The ToLower swaps on the whole NO_PROXY text and the target host are unobservable here (entries are lowercased again, and the URL parser already gave an ASCII host); they stay as Pi writes them. Trim vs TrimStart on the port is equivalent.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0b4e1e3376(2026-10-09),90882933f6(2026-10-09),a1fa6d1a33(2026-10-09) - Tests:
ai/azure_config_oracle_test.go::TestAzureBaseURLNormalizationMatchesPi,ai/codex_vertex_urls_oracle_test.go::TestCodexWebSocketAndVertexVersionURLsMatchPi,ai/node_http_proxy_oracle_test.go::TestResolveHTTPProxyURLForTargetMatchesPiOracle,internal/evals/plan_oracle_test.go::TestPlanMatchesPiOnSeededProbes - Release note: NO_PROXY entries and ports are trimmed with JavaScript's whitespace rules, as in Pi: an entry or port that starts with U+0085 is no longer stripped, and one that starts with U+FEFF is.
BF-a238095
coding: fix session executor queue-prefix race and durable execMu waits
- Source: PiG's own defect
- Symptom: coding: fix session executor queue-prefix race and durable execMu waits
- Root cause: The queue overflow prefix was snapshotted from len(rawEvents) while the executor had already received (but not finished) one event, so a reentrant queue update jumped ahead of it. The prefix now counts queued events and shrinks when one finishes.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
a23809539a(2026-10-13) - Tests: None recorded.
BF-a3859aa
An OpenAI-compatible provider that streams a tool call without an id or a function name (OpenRouter and GLM…
- Source: A Pi defect that PiG fixed first (divergence D120)
- Symptom: An OpenAI-compatible provider that streams a tool call without an id or a function name (OpenRouter and GLM do) no longer leaves the session failing every later turn with
tool messages must include a non-empty string tool_call_id. - Root cause: Pi openai-completions.ts:513-514 (id: toolCall.id || "") and :1427 (tool_call_id: toolMsg.toolCallId) replay an id-less call and its result with empty ids; providers answer 400. Give the call an id at block end and omit nameless calls and their results on replay.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10299
- Removed when Pi fixes it: Pi fills the id at stream end and omits unreplayable calls on replay.
- Fix commits:
3ef234b949(2026-10-09),82994975e6(2026-10-09),a3859aa658(2026-10-09),4180310931(2026-10-10),543dba2cbe(2026-10-10),66765600a6(2026-10-10),6693198f8c(2026-10-10),9cc4d406f7(2026-10-10),74ed845011(2026-10-11),b567ebab3c(2026-10-11) - Tests:
ai/openai_empty_tool_call_reviewer_test.go::TestOpenAICompletionsGeneratedToolCallIDsAreUnique,ai/openai_empty_tool_call_reviewer_test.go::TestOpenAICompletionsReplayOmitsStoredToolCallsWithoutIDOrNameReviewed,ai/openai_empty_tool_call_test.go::TestOpenAICompletionsEmptyToolCallDoesNotWedgeReplay,ai/unreplayable_tool_call_test.go::TestReplayOmitsUnreplayableToolCallsOnEveryProviderPath,ai/unreplayable_tool_call_test.go::TestStreamedToolCallsWithoutIDGetDistinctIDsAcrossTurns - Release note: An OpenAI-compatible provider that streams a tool call without an id or a function name (OpenRouter and GLM do) no longer leaves the session failing every later turn with
tool messages must include a non-empty string tool_call_id. PiG gives the call a unique id, leaves a call without a name or id and its results out of every provider's replayed request, and a stored session that already holds such a call works again (D120, pi-mono #10299).
BF-a3bfab2
A Radius gateway config cached on an OAuth credential is sanitized as Pi does
- Source: PiG's own defect
- Symptom: A Radius gateway config cached on an OAuth credential is sanitized as Pi does.
- Root cause: Pi reads the exact properties. baseUrl and models now come from the exact-key member map. - A gateway model was decoded through the same struct first, so "ID", "Name" or "contextwindow" after "id", "name", "contextWindow" replaced Pi's value (the model id changed). Every model is now decoded member by member by exact key (radiusGatewayModelMembers, formerly the lenient fallback only). - The fallback clamped a number member to int32: 3e9 became 2147483647 while 3000000000 decoded exactly; 1e400 (Infinity in Pi) became 0. jsNumberToInt keeps the integer part over the whole int range, with Infinity as the largest int. Mutants killed: struct-first decode, int32 clamp, no clamp, floor for trunc…
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
093cf5c781(2026-10-09),a3bfab2c03(2026-10-09),b5356c4269(2026-10-09),d1b3ad0151(2026-10-09),da99854d1b(2026-10-09) - Tests:
ai/error_body_oracle_test.go::TestErrorBodyHelpersMatchPiOnSeededProbes,ai/estimate_oracle_test.go::TestTokenEstimatesMatchPiOnSeededTranscripts,ai/overflow_oracle_test.go::TestContextOverflowMatchesPiOnSeededMessages,ai/radius_config_oracle_test.go::TestRadiusConfigMatchesPiOnSeededProbes,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage - Release note: A Radius gateway config cached on an OAuth credential is sanitized as Pi does. A model whose
contextWindowormaxTokensisnullis dropped (it was kept). A model with a fractional or out-of-range window, a non-object cost tier,headersthat are not a string map or another member Pig cannot represent is kept (it was dropped): a window keeps its integer part, clamped to the integer range, and any other such member is left at its zero value.baseUrl,modelsand each model member are read by their exact key, so a member such asIDorcontextwindowno longer replacesidorcontextWindow.
BF-a40a16d
fix(codingagent): typed input restarts the idle collection's wait on every route
- Source: PiG's own defect
- Symptom: fix(codingagent): typed input restarts the idle collection's wait on every route
- Root cause: After the frames change, a key the main loop paints synchronously or a key a selector takes through its modal route never woke the spinner, so the one forced GC could run while the user typed, about 500 ms after a run ended. D172 says input restarts the wait, as Pi-Bolt's ui.addInputListener restarts idle-gc's timer (packages/coding-agent/src/core/idle-gc.ts schedule/watchActivity, modes/interactive/interactive-mode.ts IdleCollector). The reader-side router now wakes the ticker for every input event while the wait runs; outside it (and during runs) a key costs one atomic load.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
a40a16d205(2026-10-13) - Tests: None recorded.
BF-a46f930
A Package configured as a settings object reads a resource kind its pi manifest does not declare (or…
- Source: PiG's own defect
- Symptom: A Package configured as a settings object reads a resource kind its
pimanifest does not declare (or declares empty, when patterns are given) from the conventional directory, and apimanifest field that is not an array of strings counts as absent, as in Pi'scollectPackageResources. - Root cause: TestLoadMcpConfigMatchesPiOnRandomFiles loads 1500 random global and project mcp.json files through Pi's loadMcpConfig and PiG's LoadMcpConfig: 187 differed, among them every file with a numeric server name, because Object.entries lists array-index keys first and orderedjson.Object kept file order. Object.Set now inserts an array-index key at its ascending place before the other keys (Keys, MarshalJSON, Clone follow), with TestObjectEnumeratesArrayIndexKeysFirst. Dependent packages (codingagent, extension, mcpext, mcp, packagecontent) pass; the failures left in tools and the node-backed subprocess tests are the fd/rg and node-shim environment ones.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
12cd60837e(2026-10-09),1a29f511e7(2026-10-09),3def2a7bd8(2026-10-10),7e1a05ccfd(2026-10-10),a46f930200(2026-10-10),f3b4045e8a(2026-10-10) - Tests:
coding/mcpext/config_load_oracle_test.go::TestLoadMcpConfigMatchesPiOnRandomFiles,coding/packagecontent/discover_filtered_test.go::TestDiscoverFilteredFallsBackToConventionalDirectories,internal/codingagent/migrations_tree_oracle_test.go::TestRunMigrationsTreeMatchesPi,internal/codingagent/settings_file_bytes_oracle_test.go::TestSettingsFileBytesMatchPi,internal/ignorerules/ignore_oracle_test.go::TestIgnoredMatchesPiIgnorePackage,internal/ignorerules/ignorerules_test.go::TestIgnoreRulesFollowTheIgnorePackage,internal/orderedjson/orderedjson_test.go::TestObjectEnumeratesArrayIndexKeysFirst,internal/packagemanager/resolve_oracle_test.go::TestResolveMatchesPiOnRandomFixtures - Release note: A Package configured as a settings object reads a resource kind its
pimanifest does not declare (or declares empty, when patterns are given) from the conventional directory, and apimanifest field that is not an array of strings counts as absent, as in Pi'scollectPackageResources.
BF-a4753ec
A panic in a background goroutine of an interactive session no longer ends PiG with only a stderr dump
- Source: PiG's own defect
- Symptom: A panic in a background goroutine of an interactive session no longer ends PiG with only a stderr dump.
- Root cause: Every other exit the user did not ask for now leaves a trace (D102, additive): an exit.log line in the agent directory for SIGHUP, SIGTERM, an external SIGINT, a closed terminal or input, an extension's shutdown() and a failed run; a crash record for a panic or fatal error in any goroutine (through a per-session crash-output marker the next start reconciles); and
pig diagnoselists the recent unexpected exits. The background cache warm recovers a panic and reschedules as Pi's does, the emergency terminal exit stops extension processes, and a reconciled crash keeps its real time. - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a4753ec61e(2026-10-06, #170),c019ea9155(2026-10-06, #163) - Tests:
coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,internal/codingagent/agent_env_isolation_test.go::TestPackageTestsDoNotReachTheExportedAgentDirectory,internal/codingagent/exit_record_test.go::TestBackgroundGroupPanicReachesTheHandlerWithItsStack,internal/codingagent/exit_record_test.go::TestBackgroundTaskPanicEndsTheSessionWithACrashRecord,internal/codingagent/exit_record_test.go::TestCacheWarmerDecisionPanicKeepsPisDecision,internal/codingagent/exit_record_test.go::TestCacheWarmerRefreshPanicOutsideTheCaughtRequestReachesTheUncaughtHandler,internal/codingagent/exit_record_test.go::TestCacheWarmerRefreshRequestPanicIsCaughtAndReschedules,internal/codingagent/exit_record_test.go::TestCleanEndLeavesNoMarkerand 23 more in docs/bug-ledger.json - Release note: A panic in a background goroutine of an interactive session no longer ends PiG with only a stderr dump. PiG restores the terminal, prints the error, records it in
crashes.jsonand exits 1, as Pi does for an uncaught exception. Any other goroutine panic or runtime fatal error is copied to<agent dir>/crash-output/and becomes a crash record at the next start.
BF-a4753ec-2
The prompt-cache refresh that fires minutes after a prompt now treats a panic in the extension decision or in…
- Source: PiG's own defect
- Symptom: The prompt-cache refresh that fires minutes after a prompt now treats a panic in the extension decision or in the refresh request as Pi treats a thrown error there: it keeps Pi's decision or skips that refresh and keeps warming, instead of ending the session.
- Root cause: Every other exit the user did not ask for now leaves a trace (D102, additive): an exit.log line in the agent directory for SIGHUP, SIGTERM, an external SIGINT, a closed terminal or input, an extension's shutdown() and a failed run; a crash record for a panic or fatal error in any goroutine (through a per-session crash-output marker the next start reconciles); and
pig diagnoselists the recent unexpected exits. The background cache warm recovers a panic and reschedules as Pi's does, the emergency terminal exit stops extension processes, and a reconciled crash keeps its real time. - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
a4753ec61e(2026-10-06, #170) - Tests:
coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,internal/codingagent/exit_record_test.go::TestBackgroundGroupPanicReachesTheHandlerWithItsStack,internal/codingagent/exit_record_test.go::TestBackgroundTaskPanicEndsTheSessionWithACrashRecord,internal/codingagent/exit_record_test.go::TestCacheWarmerDecisionPanicKeepsPisDecision,internal/codingagent/exit_record_test.go::TestCacheWarmerRefreshPanicOutsideTheCaughtRequestReachesTheUncaughtHandler,internal/codingagent/exit_record_test.go::TestCacheWarmerRefreshRequestPanicIsCaughtAndReschedules,internal/codingagent/exit_record_test.go::TestCleanEndLeavesNoMarker,internal/codingagent/exit_record_test.go::TestDeadTerminalExitLeavesALineWithTheReasonand 13 more in docs/bug-ledger.json - Release note: The prompt-cache refresh that fires minutes after a prompt now treats a panic in the extension decision or in the refresh request as Pi treats a thrown error there: it keeps Pi's decision or skips that refresh and keeps warming, instead of ending the session.
BF-a4753ec-3
When the terminal's input ends while its output is still there, PiG keeps running as Pi does instead of…
- Source: PiG's own defect
- Symptom: When the terminal's input ends while its output is still there, PiG keeps running as Pi does instead of exiting 1 with
Error: EOF. - Root cause: Every other exit the user did not ask for now leaves a trace (D102, additive): an exit.log line in the agent directory for SIGHUP, SIGTERM, an external SIGINT, a closed terminal or input, an extension's shutdown() and a failed run; a crash record for a panic or fatal error in any goroutine (through a per-session crash-output marker the next start reconciles); and
pig diagnoselists the recent unexpected exits. The background cache warm recovers a panic and reschedules as Pi's does, the emergency terminal exit stops extension processes, and a reconciled crash keeps its real time. - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a4753ec61e(2026-10-06, #170) - Tests:
coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,internal/codingagent/exit_record_test.go::TestBackgroundGroupPanicReachesTheHandlerWithItsStack,internal/codingagent/exit_record_test.go::TestBackgroundTaskPanicEndsTheSessionWithACrashRecord,internal/codingagent/exit_record_test.go::TestCacheWarmerDecisionPanicKeepsPisDecision,internal/codingagent/exit_record_test.go::TestCacheWarmerRefreshPanicOutsideTheCaughtRequestReachesTheUncaughtHandler,internal/codingagent/exit_record_test.go::TestCacheWarmerRefreshRequestPanicIsCaughtAndReschedules,internal/codingagent/exit_record_test.go::TestCleanEndLeavesNoMarker,internal/codingagent/exit_record_test.go::TestDeadTerminalExitLeavesALineWithTheReasonand 13 more in docs/bug-ledger.json - Release note: When the terminal's input ends while its output is still there, PiG keeps running as Pi does instead of exiting 1 with
Error: EOF.
BF-a47eb1a
A tool result from a Rust or Python extension can set the collapsed-view preview the Go SDK already had…
- Source: PiG's own defect
- Symptom: A tool result from a Rust or Python extension can set the collapsed-view
previewthe Go SDK already had (ToolResult::with_preview, or a"preview"key of the result dict), and the preview footer readsTook 1m 15sas the shell renderers do instead ofTook 75.0s. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),a47eb1a22b(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunkand 5 more in docs/bug-ledger.json - Release note: A tool result from a Rust or Python extension can set the collapsed-view
previewthe Go SDK already had (ToolResult::with_preview, or a"preview"key of the result dict), and the preview footer readsTook 1m 15sas the shell renderers do instead ofTook 75.0s.
BF-a53f72f
A context an extension kept across a session replacement or reload now raises Pi's stale message from cwd…
- Source: PiG's own defect
- Symptom: A context an extension kept across a session replacement or reload now raises Pi's stale message from
cwd,mode,hasUIandmodelin the Go, Python and Rust SDKs, as Pi throws it. - Root cause: Renames the Go types that implement packages/coding-agent InteractiveModeOptions, FooterComponent (footer.ts), ArminComponent (armin.ts) and AgentSessionServices/createAgentSessionServices (agent-session-services.ts). No behavior change; the existing tests cover them.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0dc27ec862(2026-10-06),a53f72f519(2026-10-06),e2f7a2b423(2026-10-06) - Tests:
extensions/sdk/stale_context_test.go::TestFirstInvalidateMessageWins,extensions/sdk/stale_context_test.go::TestLocalContextMembersPanicWithTheStaleMessageAfterInvalidate,extensions/sdk/stale_context_test.go::TestReplacementContextIsNotStaleAfterInvalidate,test/extension-conformance/context_stale_test.go::TestStaleContextLocalMembersRaiseTheStaleMessageInEverySDK - Release note: A context an extension kept across a session replacement or reload now raises Pi's stale message from
cwd,mode,hasUIandmodelin the Go, Python and Rust SDKs, as Pi throws it. Go and Rust panic with the message and Python raisesRuntimeError; the handler's error carries it. The Rust SDK'shandler_panicerror now includes the panic text.
BF-a5524c8
In print and JSON mode, an extension whose session_shutdown handler throws after SIGTERM or SIGHUP now has…
- Source: PiG's own defect
- Symptom: In print and JSON mode, an extension whose
session_shutdownhandler throws after SIGTERM or SIGHUP now has its error reported, as Pi does, instead of "connection closed". - Root cause: SIGTERM during a tool: the signal's disposal awaits session_shutdown while the killed tool lets the run finish. Print mode's normal teardown then closed the runtime and retired the extension host under that in-flight request, so a throwing handler was reported as "Extension error (<path>): connection closed". Pi's finally awaits disposeRuntime, which does nothing once the signal handler has started it (print-mode.ts:42-48,162-167), and the process lives until the disposal settles (print-mode.ts:58-61), so Pi prints the handler's error. The teardown now joins the signal's disposal before it closes the runtime.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a5524c8b01(2026-10-11) - Tests: None recorded.
- Release note: In print and JSON mode, an extension whose
session_shutdownhandler throws after SIGTERM or SIGHUP now has its error reported, as Pi does, instead of "connection closed".
BF-a5524c8-2
In print and JSON mode, SIGTERM or SIGHUP during a tool call stops the run where Pi does while the…
- Source: PiG's own defect
- Symptom: In print and JSON mode, SIGTERM or SIGHUP during a tool call stops the run where Pi does while the extensions'
session_shutdownhandlers finish without waiting: no further provider request and no tool result. - Root cause: SIGTERM during a tool: the signal's disposal awaits session_shutdown while the killed tool lets the run finish. Print mode's normal teardown then closed the runtime and retired the extension host under that in-flight request, so a throwing handler was reported as "Extension error (<path>): connection closed". Pi's finally awaits disposeRuntime, which does nothing once the signal handler has started it (print-mode.ts:42-48,162-167), and the process lives until the disposal settles (print-mode.ts:58-61), so Pi prints the handler's error. The teardown now joins the signal's disposal before it closes the runtime.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a5524c8b01(2026-10-11) - Tests: None recorded.
- Release note: In print and JSON mode, SIGTERM or SIGHUP during a tool call stops the run where Pi does while the extensions'
session_shutdownhandlers finish without waiting: no further provider request and no tool result. A handler that waits on a timer or I/O lets the run continue, as in Pi, and text mode then prints the run's result.
BF-a690122
PIG_PROFILE=heap and the heap_live_bytes of PIG_PROFILE=metrics count only memory PiG retains
- Source: PiG's own defect
- Symptom:
PIG_PROFILE=heapand theheap_live_bytesofPIG_PROFILE=metricscount only memory PiG retains. - Root cause: PIG_PROFILE=heap and metrics heap_live_bytes ran one runtime.GC before measuring. The first cycle only moves sync.Pool contents to the victim cache, which keeps them reachable until the next cycle, so pooled buffers counted as live: at a 1M-token session the encoding/json encoder buffer of the last provider request showed as 4.5 MB at ai/openai.go json.Marshal (test/longsession measure, GODEBUG=memprofilerate=1: heap total 17.8 MB -> 13.2 MB with the fix).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a690122479(2026-10-10) - Tests:
internal/profiling/profiling_test.go::TestHeapMeasurementsOmitPooledBuffers - Release note:
PIG_PROFILE=heapand theheap_live_bytesofPIG_PROFILE=metricscount only memory PiG retains. They now run two garbage collections before measuring. After the single collection used before, buffers that pools keep for reuse still counted as live, such as the JSON encoder buffer that holds the last provider request. At a 1M-token session that buffer is about 4.5 MB.
BF-a6932b4
fix(coding): pass a Node extension request's client identity to the provider
- Source: PiG's own defect
- Symptom: fix(coding): pass a Node extension request's client identity to the provider
- Root cause: The model-stream host call decoded no
identityoption, so a Node extension's createAgentSession (core/sdk.ts getClientIdentity) and any pi-ai call that names an identity sent no attribution headers. Decode it as ai.ClientIdentity, including an explicit null attribution. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
a6932b4c2b(2026-10-21) - Tests:
internal/codingagent/subprocess_identity_options_test.go::TestSubprocessStreamOptionsCarryTheRequestIdentity
BF-a6e0ce8
fix(codingagent): session selector page keys keep the index valid (Pi #7680)
- Source: PiG's own defect
- Symptom: fix(codingagent): session selector page keys keep the index valid (Pi #7680)
- Root cause: An earlier change let every move leave -1 over an empty list, which broke TestPR7680SessionSelectionStaysValidWhenPagingBeforeTheLoad.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
a6e0ce89e3(2026-10-10) - Tests:
internal/codingagent/select_page_keys_pr7680_test.go::TestPR7680SessionSelectionStaysValidWhenPagingBeforeTheLoad
BF-a739b6d
The Bedrock endpoint region is read from the WHATWG hostname of the base URL, as Pi's new URL does…
- Source: PiG's own defect
- Symptom: The Bedrock endpoint region is read from the WHATWG hostname of the base URL, as Pi's
new URLdoes: full-width and mixed-case hosts, tabs and newlines inside the URL, and other schemes are handled as in Pi, and a URL without a scheme no longer yields a region. - Root cause: getStandardBedrockEndpointRegion (cut from Pi's bedrock-converse-stream.ts:1223, testdata/bedrock_region.mjs) runs on 3006 seeded base URLs (regions, fips, cn partition, mixed case, full-width letters, tabs/newlines, userinfo, ports, schemes, schemeless and non-URL text). net/url differed on every URL with a tab/newline inside or a full-width/uppercase host (the WHATWG parser strips and folds them) and returned a region for a schemeless '//host' that new URL rejects (red before). bedrockEndpointRegion now reads nodeurl.ParseURL's hostname. Survivor: dropping the empty-string guard, which is equivalent because ParseURL rejects ''.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7c4d874438(2026-10-09),90882933f6(2026-10-09),a739b6dcf2(2026-10-09),d80c1cd150(2026-10-09) - Tests:
ai/azure_config_oracle_test.go::TestAzureBaseURLNormalizationMatchesPi,ai/bedrock_region_oracle_test.go::TestBedrockEndpointRegionMatchesPi,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,internal/nodeurl/url_oracle_test.go::TestParseMatchesNodeURL - Release note: The Bedrock endpoint region is read from the WHATWG hostname of the base URL, as Pi's
new URLdoes: full-width and mixed-case hosts, tabs and newlines inside the URL, and other schemes are handled as in Pi, and a URL without a scheme no longer yields a region.
BF-a7be906
Pressing Enter in the model picker's search box selects the highlighted model even when tui.select.confirm…
- Source: PiG's own defect
- Symptom: Pressing Enter in the model picker's search box selects the highlighted model even when
tui.select.confirmis rebound. - Root cause: The picker now uses NewInput with OnSubmit = selectHighlighted, shared with the confirm case.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
082355f166(2026-10-07),0ae3fcf17c(2026-10-07),a7be906e71(2026-10-07),ccaade27af(2026-10-07),f2a2b3409b(2026-10-07) - Tests:
internal/codingagent/remote_overlay_production_review_test.go::TestRemoteOverlayProductionRejectsCachedTerminalWidth,internal/codingagent/session_selector_oracle_test.go::TestSessionSelectorInputMatchesPi,internal/codingagent/thinking_selector_oracle_test.go::TestThinkingSelectorInputMatchesPi,internal/codingagent/tree_row_format_test.go::TestFormatTreeRow_AllEntryTypes,internal/codingagent/tree_selector_oracle_test.go::TestTreeSelectorInputMatchesPi,internal/codingagent/w3_pi_test.go::TestW3SessionPiComparison,test/parity/scenarios/settings/06-thinking-selector-search-editing.toml,tui/model_selector_oracle_test.go::TestModelSelectorInputMatchesPiand 1 more in docs/bug-ledger.json - Release note: Pressing Enter in the model picker's search box selects the highlighted model even when
tui.select.confirmis rebound.
BF-a7d2ef5
Path autocomplete after resuming a session created in another directory now lists files from that session's…
- Source: PiG's own defect
- Symptom: Path autocomplete after resuming a session created in another directory now lists files from that session's directory instead of the launch directory.
- Root cause: Pi builds the autocomplete provider on sessionManager.getCwd() (interactive-mode.ts:820); PiG used os.Getwd(), so after resuming a session from another directory path completion listed the launch dir.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a7d2ef5c78(2026-10-13) - Tests:
internal/codingagent/autocomplete_session_cwd_test.go::TestAutocompleteProviderCompletesPathsFromSessionCwd - Release note: Path autocomplete after resuming a session created in another directory now lists files from that session's directory instead of the launch directory.
BF-a81ec0d
A key pressed while an extension's custom overlay is open no longer freezes pig when the key reaches the…
- Source: PiG's own defect
- Symptom: A key pressed while an extension's custom overlay is open no longer freezes
pigwhen the key reaches the overlay on the main input loop. - Root cause: Every other exit the user did not ask for now leaves a trace (D102, additive): an exit.log line in the agent directory for SIGHUP, SIGTERM, an external SIGINT, a closed terminal or input, an extension's shutdown() and a failed run; a crash record for a panic or fatal error in any goroutine (through a per-session crash-output marker the next start reconciles); and
pig diagnoselists the recent unexpected exits. The background cache warm recovers a panic and reschedules as Pi's does, the emergency terminal exit stops extension processes, and a reconciled crash keeps its real time. - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
65b7c13e09(2026-10-06, #168),a4753ec61e(2026-10-06, #170),a81ec0dc55(2026-10-08),af1fe1a8fe(2026-10-08, #175),f680b9591a(2026-10-08, #197) - Tests:
coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,coding/cli/rpc_shutdown_test.go::TestRPCInputEndAfterExtensionCommandComparedWithPi,coding/extension/host/subprocess/tool_call_input_reply_test.go::TestToolCallReplyEditsTheEventInputInPlace,internal/codingagent/auth_warnings_test.go::TestCycleModelChecksAnthropicSubscriptionAuth,internal/codingagent/exit_record_test.go::TestBackgroundGroupPanicReachesTheHandlerWithItsStack,internal/codingagent/exit_record_test.go::TestBackgroundTaskPanicEndsTheSessionWithACrashRecord,internal/codingagent/exit_record_test.go::TestCacheWarmerDecisionPanicKeepsPisDecision,internal/codingagent/exit_record_test.go::TestCacheWarmerRefreshPanicOutsideTheCaughtRequestReachesTheUncaughtHandlerand 35 more in docs/bug-ledger.json - Release note: A key pressed while an extension's custom overlay is open no longer freezes
pigwhen the key reaches the overlay on the main input loop. The loop read the overlay's state by queueing a task on itself and waiting for it, so the key never reached the extension. A caller already on the main loop now runs that work inline, as Pi's single event loop does, and a task the loop posts to its own full queue no longer blocks.
BF-a8a04f4
PIG_DEBUG writes its log with mode 0600 and refuses a symlink or a file owned by another user in the shared…
- Source: PiG's own defect
- Symptom:
PIG_DEBUGwrites its log with mode 0600 and refuses a symlink or a file owned by another user in the shared temporary directory;PIG_DEBUG_KEYSlogs typed text and pastes only by length, so a key entered at a masked login prompt no longer reaches the log;pig-render.logis private (0600). - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a8a04f452d(2026-10-10) - Tests:
internal/codingagent/debug_log_security_test.go::TestRedactKeyChunkHidesTypedAndPastedText,internal/codingagent/debug_log_security_unix_test.go::TestDebugLogRefusesASymlinkAndIsPrivate - Release note:
PIG_DEBUGwrites its log with mode 0600 and refuses a symlink or a file owned by another user in the shared temporary directory;PIG_DEBUG_KEYSlogs typed text and pastes only by length, so a key entered at a masked login prompt no longer reaches the log;pig-render.logis private (0600).
BF-a8ff0c7
The anthropic-messages and google-generative-ai requests build their URL as @anthropic-ai/sdk and…
- Source: PiG's own defect
- Symptom: The anthropic-messages and google-generative-ai requests build their URL as @anthropic-ai/sdk and @google/genai do (query rewritten through qs or the form serializer, WHATWG normalisation, alt=sse placement), and a base URL with credentials is refused as fetch refuses it.
- Root cause: Not recorded.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a8ff0c7afe(2026-10-10) - Tests:
ai/sdk_request_url_oracle_test.go::TestSDKProvidersRefuseCredentialsLikeFetch,ai/sdk_request_url_oracle_test.go::TestSDKProvidersRequestURLMatchesSDK - Release note: The anthropic-messages and google-generative-ai requests build their URL as @anthropic-ai/sdk and @google/genai do (query rewritten through qs or the form serializer, WHATWG normalisation, alt=sse placement), and a base URL with credentials is refused as fetch refuses it.
BF-a901509
OpenRouter turns (the built-in provider and custom providers on OpenRouter's endpoint) now take OpenRouter's…
- Source: PiG's own defect
- Symptom: OpenRouter turns (the built-in provider and custom providers on OpenRouter's endpoint) now take OpenRouter's reported total as
usage.cost.totalfor OpenAI Completions and Anthropic Messages streams, plus the upstream inference cost for a BYOK request, instead of the catalog estimate, and mark it provider-reported. - Root cause: Ports Pi (#10286, fixes #9980). For an OpenRouter model the OpenAI Completions usage chunk and the Anthropic Messages message_delta usage replace usage.cost.total with the reported cost (plus the upstream inference cost for BYOK) and set usage.cost.totalFromProvider. Retires divergence D124, which carried the unreleased branch of this fix: the call-site markers, the export-html normalize_replace rules and the pinned-asset note are removed because template.js is now Pi's file.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.3
- Fix commits:
a9015090a5(2026-10-22) - Tests:
ai/openrouter_cost_test.go::TestAnthropicMessagesUsesOpenRouterReportedCost,ai/openrouter_cost_test.go::TestApplyOpenRouterReportedCost,ai/openrouter_cost_test.go::TestOpenAICompletionsOpenRouterReportedTotalWithoutCatalogRates,ai/openrouter_cost_test.go::TestOpenAICompletionsUsesOpenRouterReportedCost,internal/codingagent/export/upstream_test.go::TestExportHTMLPinnedAssets - Release note: OpenRouter turns (the built-in provider and custom providers on OpenRouter's endpoint) now take OpenRouter's reported total as
usage.cost.totalfor OpenAI Completions and Anthropic Messages streams, plus the upstream inference cost for a BYOK request, instead of the catalog estimate, and mark it provider-reported. The export HTML header uses the recorded total. Replaces PiG's earlier D124 form of this fix with Pi's.
BF-a94455c
The durable code mode joins the text items of a script's output in linear time (Pi f11e50f7…
- Source: PiG's own defect
- Symptom: The durable code mode joins the text items of a script's output in linear time (Pi f11e50f7, #10678): 100,000 items took about 3 s because every item read the end of the growing joined text.
- Root cause: ExecuteTool's arguments are a Go map and Execute receives one, so the key order of a script's context was lost before the provider saw it (the questions reached it alphabetical). callTool now sends the order as data, an 'order' argument of the internal model tools (a tree of keys, orderOf), and the tool rebuilds the context from it (inOrder). The row preview and the journal keep Pi's {model, context} arguments. Test: the questions zeta, alpha reach the provider in that order; the state's key order stays lost because ai.ClassifierContext.State is a Go map (changelog known issue narrowed). Mutants red: no order sent, order ignored by the tool, pairs skipped.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
3b9c8e7b11(2026-10-11),3d751efbda(2026-10-11),676874441a(2026-10-11),a94455cb5e(2026-10-12) - Tests:
durable/codemode/join_text_test.go::TestJoinAdjacentTextEqualsTheQuadraticJoinOnRandomItems,durable/codemode/join_text_test.go::TestJoinAdjacentTextIsLinearAtTheItemLimit,durable/codemode/models_test.go::TestCodeModeModels,durable/codemode/output_test.go::TestFormatResultMatchesPi,durable/harness/tool_nested_records_test.go::TestNestedErrorResultsAndStructuredOutput - Release note: The durable code mode joins the text items of a script's output in linear time (Pi f11e50f7, #10678): 100,000 items took about 3 s because every item read the end of the growing joined text. The coding-agent code mode already joined each run once.
BF-a9757c8
A Cloudflare model built with coding.BuildModel and streamed through its own Provider resolves…
- Source: PiG's own defect
- Symptom: A Cloudflare model built with
coding.BuildModeland streamed through its ownProviderresolves{CLOUDFLARE_ACCOUNT_ID}and{CLOUDFLARE_GATEWAY_ID}from the stored credential's env again, under the request env, as Pi'sModels.applyAuthmerges the resolution env beforecloudflareStreamsruns. - Root cause: Pi's Models.applyAuth merges the auth resolution env under the request env (models.ts:868) before the provider's cloudflareStreams resolves the endpoint placeholders (cloudflare-stream.ts:21-28). ai.WrapBuiltinProvider handed the wrapper only the request env, so a per-model provider whose configured Env holds the stored credential's account and gateway ids (coding.BuildModel, then model.Provider.Stream) sent the request to the literal {CLOUDFLARE_ACCOUNT_ID} URL; before the provider-ID gate was removed the API provider merged its configured Env itself.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a9757c8ed3(2026-10-10) - Tests:
ai/provider_wrap_test.go::TestWrapBuiltinProviderResolvesTheEndpointFromTheConfiguredEnv,coding/cloudflare_build_model_test.go::TestBuildModelCloudflareEndpointUsesTheStoredCredentialEnv - Release note: A Cloudflare model built with
coding.BuildModeland streamed through its ownProviderresolves{CLOUDFLARE_ACCOUNT_ID}and{CLOUDFLARE_GATEWAY_ID}from the stored credential's env again, under the request env, as Pi'sModels.applyAuthmerges the resolution env beforecloudflareStreamsruns. Sinceai.WrapBuiltinProviderthe wrapper read only the request env, so such a request went to the literal placeholder URL.
BF-aa2003c
The edit tool's prepared arguments keep the members in the order the model sent them
- Source: PiG's own defect
- Symptom: A
tool_callhandler used to see the members sorted, with<,>and&escaped. - Root cause: prepareEditArguments assigns args.edits in place and returns { ...rest, edits } (edit.ts:103-133), so the tool_call input keeps the model's member order. The Go port re-marshalled a map, sorting every member and escaping <, > and &.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
aa2003ca07(2026-10-06) - Tests:
internal/codingagent/tools/edit_diff_test.go::TestEditPrepareArgumentsKeepsMemberOrder - Release note: The edit tool's prepared arguments keep the members in the order the model sent them. Legacy
oldText/newTextarguments are folded intoeditsafter the other members, and a JSON-stringeditsstays where it was, as in Pi. Atool_callhandler used to see the members sorted, with<,>and&escaped.
BF-aa69959
Key matching follows Pi for more terminal inputs and key identifiers
- Source: PiG's own defect
- Symptom: Key matching follows Pi for more terminal inputs and key identifiers.
- Root cause:
- keys.ts's sequence patterns use \d+ (:597, :609, :620, :641, :697); Pig's Atoi accepted a sign (ESC[1;+5A, ESC[27;1;-1~ as up) and an arrow event with no digits (ESC[1;5:A). - parseInt yields a JavaScript number and the bitwise lock mask applies ToInt32, so ESC[1;4294967301A is ctrl+up in Pi; codepoints past 2^53 round (ESC[9007199254741039;5u is not ctrl+/). Pig refused or misread both. - parseKeyId (:788) ignores a part that names no modifier and lowercases with toLowerCase: meta+a matches a, hyper+up matches up, meta+f1 matches f1, ctrl+İ matches nothing. Pig matched none of the first three and read İ as i. Mutants killed: the inexact-modifier check, ToInt32 wrap, Unicode…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e8cc072521(2026-10-08),056bbfb74e(2026-10-09),2c04c1445a(2026-10-09),aa69959a64(2026-10-09),da99854d1b(2026-10-09) - Tests:
ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,chord/delta/own_key_order_test.go::TestDiffRevisionsVisitsArrayIndexKeysFirstInNumericOrder,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,internal/chord/chordjson/own_key_order_test.go::TestCopyFollowsPisKeyOrderAndMessages,internal/evals/plan_oracle_test.go::TestPlanMatchesPiOnSeededProbes,internal/evals/report_oracle_test.go::TestEvalReportMatchesPiOnSeededDesigns,tui/fuzzy_oracle_test.go::TestFuzzyMatchesPiOnSeededProbes,tui/keys_oracle_test.go::TestParseAndMatchKeyMatchPiOnSeededInputand 2 more in docs/bug-ledger.json - Release note: Key matching follows Pi for more terminal inputs and key identifiers. Unmodified
enterandtabno longer match an xterm modifyOtherKeys sequence. A sequence number with a sign (ESC [ 1 ; +5 A) or an event field with no digits (ESC [ 1 ; 5 : A) is no longer read as a key. Modifier and codepoint numbers are read as JavaScript numbers, so very large values wrap and round as in Pi. A key identifier part that names no modifier, such asmetainmeta+a, is ignored as in Pi instead of making the identifier match nothing, and identifiers are lowercased as JavaScript'stoLowerCasedoes.
BF-aabd300
An OpenAI Responses, Azure OpenAI Responses or Google stream event whose JSON is a number, string, boolean or…
- Source: PiG's own defect
- Symptom: An OpenAI Responses, Azure OpenAI Responses or Google stream event whose JSON is a number, string, boolean or array is skipped as Pi skips it, instead of ending the reply with a decode error; a null Responses event fails with Pi's TypeError message.
- Root cause: Follows finding 108, the pi-messages instance, for the other providers. TestProviderStreamNonObjectEventsMatchPi puts
data: <value>with 5, null, [1], "x" or false after the first record of 20 seeded bodies per provider and compares Pi's provider with Pig's (anthropic, openai-completions, openai-responses, azure-openai-responses, google, mistral). - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),8070fcc38b(2026-10-10),a882d85b79(2026-10-10),c4d7bd0b2f(2026-10-10),212fece3ec(2026-10-11),869cef866a(2026-10-11),aabd300c94(2026-10-11),e720ec0d0a(2026-10-11) - Tests:
ai/bedrock_eventstream_integer_float_test.go::TestBedrockStreamAcceptsIntegerValuedFloats,ai/bedrock_eventstream_integer_float_test.go::TestBedrockStreamEventReadsIntegerValuedFloats,ai/fetch_request_url_test.go::TestCodexAndAzureRequestTargetIsParsedLikeFetch,ai/fetch_request_url_test.go::TestCodexRequestTargetDropsTheFragmentOfAnOpaquePath,ai/openrouter_exchange_oracle_test.go::TestOpenRouterExchangeMatchesPi,ai/pi_messages_non_object_event_test.go::TestPiMessagesNonObjectEventIsSkippedLikeAnUnknownEvent,ai/provider_error_oracle_test.go::TestProviderErrorResponsesMatchPi,ai/provider_stream_non_object_oracle_test.go::TestProviderStreamNonObjectEventsMatchPiand 1 more in docs/bug-ledger.json - Release note: An OpenAI Responses, Azure OpenAI Responses or Google stream event whose JSON is a number, string, boolean or array is skipped as Pi skips it, instead of ending the reply with a decode error; a null Responses event fails with Pi's TypeError message.
BF-ab0ee71
Hiding an overlay whose preFocus is another overlay, such as a panel focused when the overlay was shown over…
- Source: PiG's own defect
- Symptom: Hiding an overlay whose preFocus is another overlay, such as a panel focused when the overlay was shown over it, focuses that panel as an overlay, as Pi does: its handle reports
isFocused()and its ownhide()moves focus on. - Root cause: TestCustomOverlayFocusChangesReachSDKs (bridge in place, SDK changes and pack reverted): every cell (subprocess-go, -node, -node-packed, -rust, -python, fused-go) "the component did not render focused=false after it lost focus".
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2a055a7a3a(2026-10-13),a9058bf992(2026-10-13),ab0ee713e2(2026-10-13),d87b70c54c(2026-10-13),ec2080a75f(2026-10-13) - Tests:
coding/extension/host/subprocess/node_library_bundle_test.go::TestNodeLibraryBundlesKeepSharedIdentitiesWithoutRawCatalogLoads,coding/extension/host/subprocess/runtime_node_overlay_handle_test.go::TestNodeFactoryTuiShowOverlayHandleIsSynchronous,coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodePromptEndWaitEndsWhenAnotherProcessTurnsTheLoop,coding/extension/host/subprocess/ui_bridge_raw_overlay_test.go::TestRawOverlayEndsWithItsConnection,coding/extension/host/subprocess/ui_bridge_raw_overlay_test.go::TestRawOverlayShowsAtOnceAndEndsWithItsHide,coding/extension/host/subprocess/ui_bridge_raw_overlay_test.go::TestRawOverlayWithoutTUIIsNotShown,coding/extension/host/subprocess/ui_bridge_test.go::TestFocusedCustomComponentsStack,coding/extension/host/subprocess/ui_bridge_test.go::TestInteractiveDialogOpensOverFocusedOverlayand 17 more in docs/bug-ledger.json - Release note: Hiding an overlay whose preFocus is another overlay, such as a panel focused when the overlay was shown over it, focuses that panel as an overlay, as Pi does: its handle reports
isFocused()and its ownhide()moves focus on.
BF-ab5be97
Long streamed answers render each frame faster: the Markdown view re-wraps and re-renders only the lines and…
- Source: PiG's own defect
- Symptom: Long streamed answers render each frame faster: the Markdown view re-wraps and re-renders only the lines and inline text that changed since the last frame, with the same output.
- Root cause: The main screen asks the root for the lines kept since the revision it drew last. Line resets and the differential scan start at the first line that can differ: the earlier of the kept prefix and both frames' cursor rows. The per-line reuse of earlier reset results stays, but only while the previous input array is intact: it was composed with overlays, or the root has rendered at most once since. Overlay frames keep nothing, and a frame after RestoreRenderState scans everything. Mouse dispatch reads child heights from the window.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
10aebd5c05(2026-10-09),136e3703be(2026-10-10),79eadcf623(2026-10-10),ab5be97bbe(2026-10-10) - Tests:
ai/complexity_scaling_norace_test.go::TestScalingProviderRequestPerTurn,ai/complexity_scaling_norace_test.go::TestScalingProviderStreamPerDelta,coding/cli/complexity_scaling_norace_test.go::TestScalingExtensionEventStatePush,coding/complexity_scaling_norace_test.go::TestScalingSessionTurn,coding/extension/host/subprocess/complexity_scaling_norace_test.go::TestScalingMessageUpdatePayload,internal/codingagent/compaction/complexity_scaling_norace_test.go::TestScalingCompactionAndContextEstimate,internal/codingagent/complexity_scaling_norace_test.go::TestScalingSessionLogOperations,internal/codingagent/tools/complexity_scaling_norace_test.go::TestScalingToolsand 10 more in docs/bug-ledger.json - Release note: Long streamed answers render each frame faster: the Markdown view re-wraps and re-renders only the lines and inline text that changed since the last frame, with the same output.
BF-ac0dc3b
An empty list item ends its list: a following unindented line is no longer taken for its lazy continuation…
- Source: PiG's own defect
- Symptom: An empty list item ends its list: a following unindented line is no longer taken for its lazy continuation, and spaces ending a list's last line render the empty row Pi's space token gives.
- Root cause: marked's list tokenizer breaks on a line indented less than the item when the previous line is empty, indented code, a fence/heading start or a rule (Tokenizer.list: O/h), and trims the list raw so trailing spaces on the last line leave a space token (markdown.ts case space). 11 new rows in TestMarkdownRenderMatchesPi (7 red before).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ac0dc3bee4(2026-10-10),cdf0be76e4(2026-10-10) - Tests:
tui/markdown_render_oracle_test.go::TestMarkdownRenderMatchesPi - Release note: An empty list item ends its list: a following unindented line is no longer taken for its lazy continuation, and spaces ending a list's last line render the empty row Pi's space token gives.
BF-ac6fcea
A session_start handler that calls ctx.shutdown() during /reload ends the session and exits 0, as Pi…
- Source: PiG's own defect
- Symptom: A
session_starthandler that callsctx.shutdown()during/reloadends the session and exits 0, as Pi does, instead of leaving the reload screen up. - Root cause: Pig awaits each reload step with awaitReloadStep, whose loop serviced UI tasks but never checked requestExit. requestShutdown's wake reached that loop instead of the input loop it interrupted, so the request was recorded and ignored: pig kept the "Reloading" box on screen and never exited.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ac6fceacad(2026-10-13) - Tests:
internal/codingagent/extension_ui_phase_shutdown_test.go::TestReloadEndedByShutdownDuringSDKStagingStopsTheReload,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedReloadStepEndsTheSession,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-start-reload.toml - Release note: A
session_starthandler that callsctx.shutdown()during/reloadends the session and exits 0, as Pi does, instead of leaving the reload screen up.
BF-aca35e7
In print and JSON mode, a Node extension no longer runs a timer or other callback between a prompt's last…
- Source: PiG's own defect
- Symptom: Pi-goal-x's
create_goalcould start a goal turn while the run was ending, printExtension error (<runtime>): This extension ctx is stale ...and leave the goal paused. - Root cause: Pi's print and JSON modes await each prompt and go on in the same continuation: to the next prompt, or to disposeRuntime(), which emits session_shutdown and then invalidates the runner (print-mode.ts:131-166, agent-session-runtime.ts:404-410, agent-session.ts:1379-1392). A timer or immediate an extension queues in the prompt's last call therefore runs only after its ctx is stale. PiG's Node runtime answered over IPC and returned to its event loop first, so pi-goal-x's agent_settled continuation timer could start a goal turn during disposal, which failed with "This extension ctx is stale" and paused the goal.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7aca9bede6(2026-10-07),aca35e7ff5(2026-10-08) - Tests:
coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodePromptEndCallbacksRunOnlyAfterTheRuntimeIsStale,coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodePromptEndWaitEndsWhenReloadBegins,coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodePromptEndWaitLeavesOtherRequestsRunning,coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodeRPCPromptEndCallbacksRunBeforeTheNextStep,coding/extension/host/subprocess/runtime_node_prompt_end_unix_test.go::TestNodePromptEndWaitEndsWithTheProcess,test/parity/scenarios/extensions-runtime/73-print-prompt-end-command.toml,test/parity/scenarios/extensions-runtime/73-print-prompt-end-input.toml,test/parity/scenarios/extensions-runtime/73-print-prompt-end-settled.toml - Release note: In print and JSON mode, a Node extension no longer runs a timer or other callback between a prompt's last extension call (its command, a handled input or
agent_settled) andsession_shutdown, nor betweensession_shutdownand the runtime going stale. Pi runs those steps without an event-loop turn between them. Before, pi-goal-x'screate_goalcould start a goal turn while the run was ending, printExtension error (<runtime>): This extension ctx is stale ...and leave the goal paused.
BF-ad49f9f
pig status no longer reports a shared extension name as an unhealthy duplicate
- Source: PiG's own defect
- Symptom:
pig statusno longer reports a shared extension name as an unhealthy duplicate. - Root cause:
pig statusbuilt each resource identity from the public name alone, so two Packages that both exposesrc/index.tsresolved to the same "src" name and the second one became a fatal duplicate. The loader does not work that way: it identifies an extension by its path, and DisambiguateIdentities renames the later copies ("src:2", "src:3") and loads them all, as Pi loads every extension path. A shared extension name is therefore a supported condition, andpig statusexited 1 while the session it described was healthy. - User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
ad49f9f02e(2026-10-23) - Tests:
coding/cli/status_command_test.go::TestStatusStillFailsOnDuplicateNonExtensionResourceIdentity,coding/cli/status_command_test.go::TestStatusWarnsOnDuplicatePackageExtensionIdentity - Release note:
pig statusno longer reports a shared extension name as an unhealthy duplicate. Two Packages that both exposesrc/index.tsresolve to the same"src"public name, and the loader identifies an extension by its path: it renames the later copies ("src:2") and loads them all, as Pi loads every extension path. The report keeps them enabled and moves the finding to a newwarningslist, sopig statusexits 0 andpig status --jsonstays"healthy": true. Skills, prompts and themes key their runtime set by name, so their duplicates remain errors.
BF-ad9ed64
Appending a tool result whose details are a tool's own value (a truncated read, bash output details, an…
- Source: PiG's own defect
- Symptom: Appending a tool result whose details are a tool's own value (a truncated
read,bashoutput details, an extension's details object) no longer parses the session record it has just written. - Root cause: Tests: - TestAppendToolResultWithTypedDetailsDoesNotDecodeItsRecord: 13.45x payload on the base, 7.14x without the sizing, 4.52x without the escape room, 2.26x now; ceiling 3x. Its mutants are red: no non-null check, no marshaler check, DetailsNull dropped, no deep compare, a nil map counted as non-null. Dropping the ToolResult source is equivalent, because the deep compare refuses a form without it. - TestAppendingATurnDoesNotParseItsRecordsForAccounting ceiling 300 -> 125 (111 now, 141 with the decode). - TestMessageEntryRecordKeepsNoSpareCapacity: a long record keeps at most its escape room plus the overhead. The new source-text row is red on the base (an 88600-byte record kept 17896…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ad9ed64513(2026-10-13) - Tests:
internal/codingagent/session_append_allocs_norace_test.go::TestAppendingATurnDoesNotParseItsRecordsForAccounting,internal/codingagent/session_append_typed_details_test.go::TestAppendToolResultWithTypedDetailsDoesNotDecodeItsRecord,internal/codingagent/session_held_entry_test.go::FuzzHeldMessageEntryMatchesTheRecordDecode,internal/codingagent/session_held_entry_test.go::TestHeldMessageEntryMatchesTheRecordDecode,internal/codingagent/session_line_capacity_test.go::TestMessageEntryRecordKeepsNoSpareCapacity,test/perfgate/process_test.go::TestRPCTurnProcessGate - Release note: Appending a tool result whose details are a tool's own value (a truncated
read,bashoutput details, an extension's details object) no longer parses the session record it has just written. Appending a 1 MiB truncated read now takes about 3 ms instead of 72 ms.
BF-ad9ed64-2
A large message's session record no longer keeps a regrown buffer when its text has escapes such as newlines…
- Source: PiG's own defect
- Symptom: A truncated 1 MiB read used to keep 1.6 MB of unused space for the life of the session and now keeps about 160 KB; a 1 MiB source-code message kept 226 KB and now keeps 88 KB.
- Root cause: Tests: - TestAppendToolResultWithTypedDetailsDoesNotDecodeItsRecord: 13.45x payload on the base, 7.14x without the sizing, 4.52x without the escape room, 2.26x now; ceiling 3x. Its mutants are red: no non-null check, no marshaler check, DetailsNull dropped, no deep compare, a nil map counted as non-null. Dropping the ToolResult source is equivalent, because the deep compare refuses a form without it. - TestAppendingATurnDoesNotParseItsRecordsForAccounting ceiling 300 -> 125 (111 now, 141 with the decode). - TestMessageEntryRecordKeepsNoSpareCapacity: a long record keeps at most its escape room plus the overhead. The new source-text row is red on the base (an 88600-byte record kept 17896…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ad9ed64513(2026-10-13) - Tests:
internal/codingagent/session_append_allocs_norace_test.go::TestAppendingATurnDoesNotParseItsRecordsForAccounting,internal/codingagent/session_append_typed_details_test.go::TestAppendToolResultWithTypedDetailsDoesNotDecodeItsRecord,internal/codingagent/session_held_entry_test.go::FuzzHeldMessageEntryMatchesTheRecordDecode,internal/codingagent/session_held_entry_test.go::TestHeldMessageEntryMatchesTheRecordDecode,internal/codingagent/session_line_capacity_test.go::TestMessageEntryRecordKeepsNoSpareCapacity,test/perfgate/process_test.go::TestRPCTurnProcessGate - Release note: A large message's session record no longer keeps a regrown buffer when its text has escapes such as newlines, tabs or quotes. A truncated 1 MiB read used to keep 1.6 MB of unused space for the life of the session and now keeps about 160 KB; a 1 MiB source-code message kept 226 KB and now keeps 88 KB.
BF-aeaaf97
Model requests reuse their HTTP connection
- Source: PiG's own defect
- Symptom: Every provider build used to create its own transport, so each request and tool round trip opened a new TCP (and TLS, and proxy CONNECT) connection and left an idle socket with its goroutines for two minutes.
- Root cause: Pi installs one process-wide dispatcher (core/http-dispatcher.ts configureHttpDispatcher), so requests reuse connections. PiG built a transport per provider: 20 requests opened 20 TCP connections and left idle sockets and goroutines. Cache the transport by TLS mode, idle timeout and proxy environment.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
04b10bcaa0(2026-10-09),aeaaf97297(2026-10-09) - Tests:
ai/http_transport_reuse_test.go::TestProvidersReuseOneConnectionAcrossBuilds,ai/http_transport_reuse_test.go::TestStreamingTransportIsKeyedByItsSettings,coding/model_runtime_conn_reuse_test.go::TestModelRuntimeReusesOneConnectionAcrossRequests - Release note: Model requests reuse their HTTP connection. Every provider build used to create its own transport, so each request and tool round trip opened a new TCP (and TLS, and proxy CONNECT) connection and left an idle socket with its goroutines for two minutes. Providers now share one transport per TLS mode; a changed idle timeout or proxy environment replaces it, as Pi's configureHttpDispatcher replaces its single dispatcher.
BF-af1fe1a
Cycling models in the interactive UI goes through the Session as in Pi: the new model persists as the…
- Source: PiG's own defect
- Symptom: Cycling models in the interactive UI goes through the Session as in Pi: the new model persists as the default, extends a non-empty
enabledModelsscope, keeps scope keys for model IDs that start with their provider, and checks Anthropic subscription auth after the switch. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
af1fe1a8fe(2026-10-08, #175) - Tests:
internal/codingagent/auth_warnings_test.go::TestCycleModelChecksAnthropicSubscriptionAuth,internal/codingagent/model_cycle_parity_test.go::TestInteractiveCycleStatusMatchesSessionResult,internal/codingagent/model_cycle_parity_test.go::TestInteractiveCycleUsesSessionSelections,internal/codingagent/scoped_models_selection_test.go::TestScopedModelStartupKeepsProviderPrefixedIDs,internal/codingagent/scoped_models_selection_test.go::TestScopedSelectionKeepsProviderPrefixedIDsInSessionScope,internal/codingagent/scoped_models_selection_test.go::TestScopedSelectionSynchronizesSessionScope,test/parity/scenarios/model-resolver-selector/26-interactive-cycle-custom-scope.toml - Release note: Cycling models in the interactive UI goes through the Session as in Pi: the new model persists as the default, extends a non-empty
enabledModelsscope, keeps scope keys for model IDs that start with their provider, and checks Anthropic subscription auth after the switch.
BF-af36fe8
A streamed tool call whose arguments are valid JSON but not an object ([], null, 5, "s") keeps them…
- Source: PiG's own defect
- Symptom: A streamed tool call whose arguments are valid JSON but not an object (
[],null,5,"s") keeps them as the model sent them, so the model is toldroot: must be object ... Received arguments: []and the session stores them, as Pi does; they used to be replaced by{}and reported as missing required properties. - Root cause: parseStreamingJson returns the value JSON.parse reads. PiG replaced [] / null / 5 / "s" with {}, so the model was told path was missing instead of root: must be object, and the session stored {}. ToolCall keeps the value's JSON text beside the nil Arguments (nonObjectArguments): ArgumentsJSON, MarshalJSON, UnmarshalJSON, the streaming parsers, clones and validation use it. The partial parser's null stays the empty object (result ?? {}); the nested-call recorder keeps arguments ?? {}.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
af36fe827b(2026-10-10) - Tests:
ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunk,ai/tool_call_argument_order_test.go::TestSetStreamingArgumentsKeepsMemberOrder,ai/tool_call_non_object_arguments_test.go::TestStreamedToolArgumentsThatAreNotAnObjectAreKeptAsPiKeepsThem - Release note: A streamed tool call whose arguments are valid JSON but not an object (
[],null,5,"s") keeps them as the model sent them, so the model is toldroot: must be object ... Received arguments: []and the session stores them, as Pi does; they used to be replaced by{}and reported as missing required properties.
BF-afa1dd5
Amazon Bedrock requestMetadata keeps its keys' insertion order on the wire, as Pi's AWS SDK writes the…
- Source: PiG's own defect
- Symptom: Amazon Bedrock
requestMetadatakeeps its keys' insertion order on the wire, as Pi's AWS SDK writes the object. - Root cause: Lead ruling 2026-10-11T00:27Z: the caller's insertion order of requestMetadata reaches the wire (parity, no D-number). Pi passes options.requestMetadata to the AWS SDK, which writes the object in its own-key order (bedrock-converse-stream.ts; ConverseStreamRequest$).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
afa1dd5185(2026-10-10),f02269c6e1(2026-10-10) - Tests:
ai/anthropic_replaced_payload_order_oracle_test.go::TestAnthropicReplacedPayloadKeepsPiMemberOrder,ai/bedrock_request_body_oracle_test.go::TestBedrockRequestBodyMatchesPi,ai/bedrock_wire_metadata_order_test.go::TestBedrockStringMapEnumeratesLikeAJavaScriptObject,ai/google_request_body_oracle_test.go::TestGoogleRequestBodyMatchesPi,coding/extension/host/subprocess/native_stream_options_union_test.go::TestNativeProviderOptionsKeepRawValuesAndNumericPresence,coding/extension/host/subprocess/request_metadata_order_test.go::TestCompleteRequestKeepsRequestMetadataOrder,coding/extension/host/subprocess/request_metadata_order_test.go::TestModelStreamRequestKeepsRequestMetadataOrder,coding/extension/host/subprocess/request_metadata_order_test.go::TestProviderOptionsWireKeepsRequestMetadataOrderand 1 more in docs/bug-ledger.json - Release note: Amazon Bedrock
requestMetadatakeeps its keys' insertion order on the wire, as Pi's AWS SDK writes the object. Go callers set the newStreamOptions.RequestMetadataOrder(keys missing from it follow in sorted order); an extension'srequestMetadatakeeps its order throughstreamModel,completeand an extension provider's options.
BF-afa1dd5-2
make port-lint has three more checks: regexpinfunc (a constant pattern compiled inside a function)…
- Source: PiG's own defect
- Symptom:
make port-linthas three more checks:regexpinfunc(a constant pattern compiled inside a function),jsonclone(a value encoded to JSON and decoded again in one function) andctxbackground(context.Background()/TODO()in a library function with no context). - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),afa1dd5185(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/bedrock_request_body_oracle_test.go::TestBedrockRequestBodyMatchesPiand 10 more in docs/bug-ledger.json - Release note:
make port-linthas three more checks:regexpinfunc(a constant pattern compiled inside a function),jsonclone(a value encoded to JSON and decoded again in one function) andctxbackground(context.Background()/TODO()in a library function with no context). Today's findings are in the ratchet baseline.
BF-affc944
Bedrock requests that fail with The pending stream has been canceled after a stalled HTTP/2 connection are…
- Source: A fix Pi shipped that PiG ported
- Symptom: Bedrock requests that fail with
The pending stream has been canceledafter a stalled HTTP/2 connection are retried. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
affc944ad8(2026-10-05) - Tests:
ai/assistant_retry_test.go::TestIsRetryableAssistantErrorClassification,mcp/oauth/oauth_test.go::TestMCPOAuthRegistersWithAnApplicationTypeDerivedFromTheRedirectURIsUnlessOneIsSet - Release note: Bedrock requests that fail with
The pending stream has been canceledafter a stalled HTTP/2 connection are retried.
BF-b02876b
chord/host: a link that is already closed when a host accepts or routes to it is no longer kept as a…
- Source: PiG's own defect
- Symptom: A route that returned a dead link (a child that exited before its link was wrapped) made every later call to that host name fail with
unavailableuntil restart, and started a ping timer for the dead connection (Pi 7f022043). - Root cause: A route that returned a dead link (a child that exited before its link was wrapped) left a closed connection in the host, so every later call to that host name failed with unavailable until restart. addConnection no longer keeps a connection whose link closed in newConnection, childNamed skips closed connections, and newConnection starts no ping timer on a closed link, as Pi's connection.ts and host.ts now do. TestLinksARouteThatReturnsAnAlreadyClosedLinkDoesNotPoisonTheHostName ports the new link.test.ts case.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b02876b1d5(2026-10-13) - Tests:
chord/host/link_upstream_test.go::TestLinksARouteThatReturnsAnAlreadyClosedLinkDoesNotPoisonTheHostName,chord/host/link_upstream_test.go::TestLinksAnAlreadyClosedLinkIsNotKeptAndStartsNoPinger - Release note:
chord/host: a link that is already closed when a host accepts or routes to it is no longer kept as a connection. Before, a route that returned a dead link (a child that exited before its link was wrapped) made every later call to that host name fail withunavailableuntil restart, and started a ping timer for the dead connection (Pi 7f022043).
BF-b0608a9
OpenRouter generated-image data URLs are matched exactly as Pi does (^data:([^;]+);base64,(.+)$): a URL…
- Source: PiG's own defect
- Symptom: OpenRouter generated-image data URLs are matched exactly as Pi does (
^data:([^;]+);base64,(.+)$): a URL with media type parameters, an empty media type or a line break in the payload is skipped, and a payload that is not valid base64 is kept instead of dropped. - Root cause: openrouter-images.ts:101 /^data:(;+);base64,(.+)$/ : no media type parameters, no empty media type, no line terminator, payload not validated.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b0608a98b9(2026-10-10) - Tests:
ai/openrouter_images_data_url_test.go::TestParseDataImageURLMatchesPiRegex - Release note: OpenRouter generated-image data URLs are matched exactly as Pi does (
^data:([^;]+);base64,(.+)$): a URL with media type parameters, an empty media type or a line break in the payload is skipped, and a payload that is not valid base64 is kept instead of dropped.
BF-b06ad92
An MCP server url can use environment variables ("url": "${TOOLS_URL}"): PiG checks and connects to the…
- Source: PiG's own defect
- Symptom: An MCP server
urlcan use environment variables ("url": "${TOOLS_URL}"): PiG checks and connects to the expanded URL, as it does forheadersandenv(Pi #10654). - Root cause: validateMcpServerConfig, createDefaultTransport and oauthUrl use the raw url; headers and env are resolved. ExpandMcpURL expands $VAR/${VAR}/$$ when fully resolvable, else keeps the url as written.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b06ad928d7(2026-10-09) - Tests:
coding/extension/mcp_url_expansion_test.go::TestExpandMcpURL,coding/extension/mcp_url_expansion_test.go::TestValidateMcpServerConfigChecksTheExpandedURL,coding/mcpext/connection_test.go::TestMCPConnectionsExpandTheServerURLReferences - Release note: An MCP server
urlcan use environment variables ("url": "${TOOLS_URL}"): PiG checks and connects to the expanded URL, as it does forheadersandenv(Pi #10654).
BF-b18a7ff
ctx.compact answers onComplete with the compaction result's members in Pi's order (summary…
- Source: PiG's own defect
- Symptom:
ctx.compactanswersonCompletewith the compaction result's members in Pi's order (summary,firstKeptEntryId,tokensBefore,estimatedTokensAfter,usage,details); they were written alphabetically. - Root cause: types.ts CompactOptions.onComplete and SessionBeforeCompactResult.compaction carry compaction.ts CompactionResult (summary, firstKeptEntryId, tokensBefore, estimatedTokensAfter?, usage?, details?). extension.CompactionResult was
any, and Session.CompactForExtension built a map[string]any, which JSON-marshals its members alphabetically; Pi's object lists them in the order above. It is now a struct with Pi's member names and order; SessionBeforeCompactResult.Compaction is *CompactionResult; the 17 test handlers that returned a map build the struct. Production reads the result through JSON (session_compaction_extensions.go), unchanged. - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b18a7ffd06(2026-10-07) - Tests:
coding/session_compact_extension_test.go::TestCompactForExtensionReturnsUpstreamResultShape - Release note:
ctx.compactanswersonCompletewith the compaction result's members in Pi's order (summary,firstKeptEntryId,tokensBefore,estimatedTokensAfter,usage,details); they were written alphabetically.
BF-b2b0c63
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: The lone-surrogate fidelity moves to the root: chordjson's default string unquote reads a surrogate escape as its unit, so delta.DecodeJson callers need no wrapper, durable/json.go and nested_result.go are back to main's calls (the nested result decodes with delta.DecodeJson and so keeps document order), NestedToolCallRecord is main's text again, and the stale any-trap entries are deleted.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
b2b0c63c54(2026-10-25) - Tests: None recorded.
BF-b2c219f
fix(runtimecell): satisfy the divergence, port and perf gates for the usage-lease and fingerprint changes
- Source: PiG's own defect
- Symptom: fix(runtimecell): satisfy the divergence, port and perf gates for the usage-lease and fingerprint changes
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
b2c219f432(2026-10-20) - Tests:
coding/extension/host/subprocess/sdk_fingerprint_marker_test.go::TestBuilderRecordsTheSDKFingerprintByReplacingTheMarker
BF-b341a82
go install github.com/MichaelKinsy/PiG/cmd/pig@latest works again
- Source: PiG's own defect
- Symptom:
go install github.com/MichaelKinsy/PiG/cmd/pig@latestworks again. - Root cause: The 0.5.0 Go module was larger than the Go module size limit because it carried parity ledgers for every earlier Pi pin; only the current pin's ledgers ship now, and v0.5.0 is retracted.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
b341a82e9d(2026-10-19, #221) - Tests: None recorded.
- Release note:
go install github.com/MichaelKinsy/PiG/cmd/pig@latestworks again. The 0.5.0 Go module was larger than the Go module size limit because it carried parity ledgers for every earlier Pi pin; only the current pin's ledgers ship now, and v0.5.0 is retracted.
BF-b3543f8
Fixed MCP stdio servers staying alive after PiG exited because the terminal closed, it crashed, or it…
- Source: PiG's own defect
- Symptom: Fixed MCP stdio servers staying alive after PiG exited because the terminal closed, it crashed, or it received a termination signal.
- Root cause: Fixed MCP stdio servers staying alive after PiG exited because the terminal closed, it crashed, or it received a termination signal.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1cbd9b094c(2026-10-07),b3543f848c(2026-10-07) - Tests:
ai/abort_race_test.go::TestOperationSignalKeepsASignalAndSuppliesAnUncancelledOne,ai/abort_race_test.go::TestRaceWithAbortSignalSettlesWithTheFirstOfOperationAndAbort,ai/abort_utils_oracle_test.go::TestAbortUtilsMatchPi,internal/codingagent/tools/shell_tracked_test.go::TestKillTrackedDetachedChildrenEndsRunningMCPStdioServers,test/parity/scenarios/slash-commands/16-mcp-manager-while-connecting.toml - Release note: Fixed MCP stdio servers staying alive after PiG exited because the terminal closed, it crashed, or it received a termination signal. Pi ends their process groups from an exit hook; PiG now does the same on those exits.
BF-b430c7a
Extension widgets now appear in the order they were set when Node or other extension-process widgets and Go…
- Source: PiG's own defect
- Symptom: Extension widgets now appear in the order they were set when Node or other extension-process widgets and Go in-process widgets share a side of the editor, as in Pi.
- Root cause: Pi's renderWidgets walks one Map per side in insertion order. PiG listed the extension-process widgets before the host-process ones. Widgets of both hosts now take a shared set sequence, and a replaced or moved one moves to the end.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
b430c7ae8d(2026-10-13) - Tests:
internal/codingagent/widget_layout_test.go::TestWidgetsFollowTheOrderTheyWereSetAcrossExtensionHosts - Release note: Extension widgets now appear in the order they were set when Node or other extension-process widgets and Go in-process widgets share a side of the editor, as in Pi. The extension-process widgets came first.
BF-b4504dd
Amazon Bedrock request bodies match Pi's: PiG writes the ConverseStream body itself in the member order Pi's…
- Source: PiG's own defect
- Symptom: Amazon Bedrock request bodies match Pi's: PiG writes the ConverseStream body itself in the member order Pi's AWS SDK writes (
messages,system,inferenceConfig,toolConfig,additionalModelRequestFields;rolebeforecontent;toolUseId,name,input), tool schemas and tool call inputs keep their authored member order, and a tool schema with an empty property name no longer fails the request. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),da99854d1b(2026-10-09),2309604d80(2026-10-10),25a75ebc88(2026-10-10),8e4a778324(2026-10-10),b4504dd82c(2026-10-10),b4e83dbe17(2026-10-10),c10cde302d(2026-10-10),eeac307095(2026-10-10),f29e906cb3(2026-10-10),fc886e3bd2(2026-10-10),fe16108cc7(2026-10-10) - Tests:
agent/assistant_member_order_oracle_test.go::TestAssistantLateMembersFollowPiCreationOrder,agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowClosesand 20 more in docs/bug-ledger.json - Release note: Amazon Bedrock request bodies match Pi's: PiG writes the ConverseStream body itself in the member order Pi's AWS SDK writes (
messages,system,inferenceConfig,toolConfig,additionalModelRequestFields;rolebeforecontent;toolUseId,name,input), tool schemas and tool call inputs keep their authored member order, and a tool schema with an empty property name no longer fails the request.
BF-b47b6bc
fix(tui): number the editor wrap divergence D159 (lead block), cite Pi's measured RangeError; add…
- Source: PiG's own defect
- Symptom: fix(tui): number the editor wrap divergence D159 (lead block), cite Pi's measured RangeError; add text-component width fuzz
- Root cause: FuzzTextComponentsStayWithinWidth (Text, TruncatedText, Input, Box, WrapTextWithAnsi, TruncateToWidth; 60s green) added.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
b47b6bccd1(2026-10-10) - Tests:
tui/components_fuzz_test.go::FuzzTextComponentsStayWithinWidth
BF-b4822ef
pig install <dir> now installs a local Go, Rust or Python extension folder and loads it as an extension, as…
- Source: PiG's own defect
- Symptom: It refused the folder with "is an extension, not a package".
- Root cause: Pi's resolveLocalExtensionSource loads a local directory that declares no filter, "pi" manifest or conventional resource directory as one extension (package-manager.ts:1377-1384), and Pig does so since. D57's refusal of a local Go, Rust or Python extension folder ("installing it as one loads nothing") was therefore false. VerifyContributesResources now refuses only npm and Git roots, where Pi's collectPackageResources has no such fallback (package-manager.ts:1317-1340).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b4822efa1d(2026-10-09) - Tests:
coding/cli/package_install_empty_test.go::TestInstallRecordsALocalExtensionDirectoryGivenRelativeToTheWorkingDirectory,coding/cli/package_install_empty_test.go::TestInstallRejectsAnExtensionDirectoryAsAPackage - Release note:
pig install <dir>now installs a local Go, Rust or Python extension folder and loads it as an extension, as it already did for a Node folder. Before, it refused the folder with "is an extension, not a package". An npm or Git source whose root is a bare extension is still refused (D57).
BF-b4c6569
~/dir/ expands to the home directory with the trailing separator kept ($HOME/dir/), as Node's path.join…
- Source: PiG's own defect
- Symptom:
~/dir/expands to the home directory with the trailing separator kept ($HOME/dir/), as Node'spath.joindoes in Pi's path normalization; it was cleaned to$HOME/dir. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
b4c65694dc(2026-10-12) - Tests: None recorded.
- Release note:
~/dir/expands to the home directory with the trailing separator kept ($HOME/dir/), as Node'spath.joindoes in Pi's path normalization; it was cleaned to$HOME/dir.
BF-b4e83db
Anthropic tools list eager_input_streaming and strict before input_schema, and the input schema and…
- Source: PiG's own defect
- Symptom: Anthropic tools list
eager_input_streamingandstrictbeforeinput_schema, and the input schema and Google function declarations keep the tool's member order (type,properties,requiredfor the legacy Anthropic schema), as Pi does. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0ae4683246(2026-10-08),e8cc072521(2026-10-08),433d232815(2026-10-09),2309604d80(2026-10-10),b4e83dbe17(2026-10-10),f29e906cb3(2026-10-10),fe16108cc7(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/anthropic_tool_schema_order_oracle_test.go::TestAnthropicToolsFollowPiMemberOrderand 13 more in docs/bug-ledger.json - Release note: Anthropic tools list
eager_input_streamingandstrictbeforeinput_schema, and the input schema and Google function declarations keep the tool's member order (type,properties,requiredfor the legacy Anthropic schema), as Pi does. A strict schema addsrequiredandadditionalPropertiesafter the members it was authored with, also when those were authored in sorted order.
BF-b4f1d4a
Pasting a large text that arrives in many terminal reads no longer stalls input: the paste is joined once…
- Source: PiG's own defect
- Symptom: Pasting a large text that arrives in many terminal reads no longer stalls input: the paste is joined once instead of being copied and searched again on every read.
- Root cause: StdinBuffer kept upstream's pasteBuffer as one string (stdin-buffer.ts:324-343: pasteBuffer += buffer, then indexOf(BRACKETED_PASTE_END)). Each read copied, canonicalized and searched the whole paste so far, so a paste that arrives over n reads cost O(n^2): a 3.8 MB paste in 5,000 reads allocated 12 GB and took 7.1 s.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b4f1d4a41a(2026-10-10) - Tests:
tui/complexity_scaling_norace_test.go::TestScalingBracketedPaste,tui/stdin_buffer_paste_chunks_test.go::TestPasteOverManyReadsEqualsOnePasteBuffer - Release note: Pasting a large text that arrives in many terminal reads no longer stalls input: the paste is joined once instead of being copied and searched again on every read.
BF-b54f9aa
The tree selector label time of a session entry whose timestamp is not a valid date prints "aN/NaN/NaN…
- Source: PiG's own defect
- Symptom: The tree selector label time of a session entry whose timestamp is not a valid date prints "aN/NaN/NaN NaN:NaN", and a timestamp is read in any form Date.parse accepts, not only RFC 3339.
- Root cause: tree-selector.ts:862-885 builds the label time from
new Date(timestamp). Go parsed four RFC 3339 layouts only (any other date form gave no label time), returned "" for an invalid date where Pi prints "aN/NaN/NaN NaN:NaN" (NaN hours, month and day, and "NaN".slice(-2)), and padded the two-digit year with %02d of year%100 where Pi slices the last two characters of the year ("5" for year 5, "-5" for year -5). formatLabelTimestamp reads the timestamp with ai.DateParse and slices the year string. An entry written by Pi always carries an ISO timestamp, so this shows for edited or foreign session files; the old test that pinned "" for an unparseable string had no upstream basis and now pins Pi's… - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b54f9aa86a(2026-10-11) - Tests:
tui/tree_label_timestamp_oracle_test.go::TestFormatLabelTimestampMatchesPi,tui/tree_select_test.go::TestFormatLabelTimestampForms - Release note: The tree selector label time of a session entry whose timestamp is not a valid date prints "aN/NaN/NaN NaN:NaN", and a timestamp is read in any form Date.parse accepts, not only RFC 3339.
BF-b5b7b43
Frontmatter YAML parsing matches the yaml library on text outside ASCII and on unterminated quotes: an…
- Source: PiG's own defect
- Symptom: Frontmatter YAML parsing matches the
yamllibrary on text outside ASCII and on unterminated quotes: an unterminated quoted scalar reports its error at the end of the input, a line that starts with a non-ASCII character no longer changes the continuation indent of the lines after it,Invalid escape sequencequotes UTF-16 units, an implicit key's 1024-character limit counts UTF-16 units, anchors may not contain Unicode whitespace, and-0is0as an!!omapor!!setkey. - Root cause: TestParseMatchesTheLibraryOnMutatedCorpus and TestLexerMatchesTheLibraryOnMutatedCorpus compare eemeli/yaml 2.9.0 with Parse/lex on 6000 mutated corpus texts (40 differed before, 15-17 per 20000 after the first fixes). Fixed: an unterminated quote (buffer.substring(0, -1) is empty: the error is at the end of input, not after the first line); a line starting with a non-ASCII character reduced the continuation indent because the lexer looked at the second byte, not the second UTF-16 unit (charAt(1)); Invalid escape sequence raw text and the 1024-unit implicit key limit counted bytes; anchors reject JavaScript whitespace (RE2 \s is narrower); -0 is 0 as an omap/set key; Buffer.from(.., base64)…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
10e9b5c109(2026-10-09),24232ef878(2026-10-09),2b3403045b(2026-10-09),ba3f1c51ac(2026-10-09),d07038cb99(2026-10-09),919575a79c(2026-10-10),b5b7b43f82(2026-10-10) - Tests:
ai/classifier_answers_oracle_test.go::TestClassifierAnswersMatchPi,ai/provider_retry_matrix_oracle_test.go::TestProviderRetryMatchesPi,ai/system_one_shared_test.go::TestSystemOneSharedFailureMessages,coding/nested_recorder_oracle_test.go::TestNestedCallRecorderMatchesPi,coding/rpcclient/jsonl_reader_oracle_test.go::TestJSONLReaderMatchesPi,internal/codingagent/compaction/compaction_oracle_test.go::TestCompactionMatchesPi,internal/codingagent/paths_oracle_test.go::TestPathsMatchPi,internal/codingagent/session_file_bytes_oracle_test.go::TestSessionFileBytesMatchPiand 11 more in docs/bug-ledger.json - Release note: Frontmatter YAML parsing matches the
yamllibrary on text outside ASCII and on unterminated quotes: an unterminated quoted scalar reports its error at the end of the input, a line that starts with a non-ASCII character no longer changes the continuation indent of the lines after it,Invalid escape sequencequotes UTF-16 units, an implicit key's 1024-character limit counts UTF-16 units, anchors may not contain Unicode whitespace, and-0is0as an!!omapor!!setkey.
BF-b5ebeb5
A resumed session draws its ! and !! command blocks, as Pi does
- Source: PiG's own defect
- Symptom: A resumed session draws its
!and!!command blocks, as Pi does. - Root cause: Every other exit the user did not ask for now leaves a trace (D102, additive): an exit.log line in the agent directory for SIGHUP, SIGTERM, an external SIGINT, a closed terminal or input, an extension's shutdown() and a failed run; a crash record for a panic or fatal error in any goroutine (through a per-session crash-output marker the next start reconciles); and
pig diagnoselists the recent unexpected exits. The background cache warm recovers a panic and reschedules as Pi's does, the emergency terminal exit stops extension processes, and a reconciled crash keeps its real time. - User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a4753ec61e(2026-10-06, #170),ba9c37b247(2026-10-06, #171),4610c9f4a1(2026-10-08, #189),b5ebeb58d1(2026-10-08, #191) - Tests:
coding/cli/rpc_initial_tools_test.go::TestRPCFreshPromptDeclaresStructuredSystemOnce,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleBusyEntriesAreSkippedNotErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledBeforeStartTouchesNothing,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledDuringTheLastEntryReportsTheCancellation,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleEntryLeasedAfterClassificationIsSkippedNotAnError,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleOtherErrorsStayErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleStopsWhenItsContextIsCancelled,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestIsBusyErrorLooksThroughWrappedJoinsand 39 more in docs/bug-ledger.json - Release note: A resumed session draws its
!and!!command blocks, as Pi does.--session,--continue,/resume,/tree,/fork,/clone,/reloadand a compaction rebuild drew nothing for a persisted command. Each block now shows its output, exit status, cancelled or truncation line as when it ran, starts collapsed, and keeps the dim borders of a!!command.
BF-b5fb4aa
fix(tui): a streamed Markdown is not resumed past a paragraph that a later LaTeX closer clips; keep wraps…
- Source: PiG's own defect
- Symptom: fix(tui): a streamed Markdown is not resumed past a paragraph that a later LaTeX closer clips; keep wraps across the trailing space row; regenerate interface inventories
- Root cause:
- resumeBarrier now also stops at a line whose rest after its first character opens block LaTeX (Lexer.ts:241-270 with markdown.ts:127-130 latexBlock start()); one helper, latexClipsFirstCharacter, serves the clip and the barrier. - The wrap memo rotates once per render (keepWraps), so the trailing space row's layout no longer drops it.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
b5fb4aad04(2026-10-10) - Tests:
tui/markdown_resume_test.go::TestResumedMarkdownEqualsAFreshRender,tui/markdown_streaming_reuse_test.go::TestStreamedMarkdownWrapsOnlyChangedLines
BF-b607c35
Started from a directory reached through a symlink (a $PWD naming a link), pig now uses the physical…
- Source: PiG's own defect
- Symptom: Started from a directory reached through a symlink (a
$PWDnaming a link), pig now uses the physical working directory as Pi does, so@filearguments are named by their physical path. - Root cause: main.ts:593 takes cwd from process.cwd() (getcwd(3)); os.Getwd returns $PWD when it names the same directory through a symlink, so a relative @file argument was named <file name="<link>/a.txt"> where Pi writes the physical path. Use nodepath.Process(), which already mirrors process.cwd().
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b607c35564(2026-10-13) - Tests:
coding/cli/startup_physical_cwd_test.go::TestStartupWorkingDirectoryIsThePhysicalPath,internal/codingagent/file_processor_oracle_test.go::TestProcessCLIFileArgumentsMatchesPi - Release note: Started from a directory reached through a symlink (a
$PWDnaming a link), pig now uses the physical working directory as Pi does, so@filearguments are named by their physical path.
BF-b684b81
RPC and session JSON write an assistant message's members in the order Pi's providers assign them…
- Source: PiG's own defect
- Symptom: RPC and session JSON write an assistant message's members in the order Pi's providers assign them:
reasoningbeforetotalTokensin usage, andresponseIdaftertimestamp, in message events,get_messages,get_entries,get_treeand the session file. - Root cause: A provider builds its output as role, content, api, provider, model, usage, stopReason, timestamp and assigns responseId, responseModel and errorMessage afterwards (packages/ai/src/api/openai-completions.ts output literal and :564-566; the catch block's errorMessage). PiG's encoder wrote responseModel, responseId, providerThinkingLevel and diagnostics before usage, so the session file and the get_entries and get_tree wire differed from Pi in member order. The order now equals the event encoder's. Found by the rpc-session journey.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
3ec34c481e(2026-10-09),412f886f75(2026-10-09),b684b81d72(2026-10-09) - Tests:
agent/message_json_member_order_test.go::TestAssistantMessageJSONKeepsProviderMemberOrder,coding/cli/rpc_event_bytes_test.go::TestRPCGetMessagesKeepsPiMemberOrder,coding/cli/rpc_event_bytes_test.go::TestRPCUsageWireOrderPutsReasoningBeforeTotalTokens - Release note: RPC and session JSON write an assistant message's members in the order Pi's providers assign them:
reasoningbeforetotalTokensin usage, andresponseIdaftertimestamp, in message events,get_messages,get_entries,get_treeand the session file.
BF-b6c65b3
! command and RPC bash output no longer keeps fragments of color codes, such as a stray m, when an…
- Source: A fix Pi shipped that PiG ported
- Symptom:
!command and RPCbashoutput no longer keeps fragments of color codes, such as a straym, when an escape sequence is split across output chunks. - Root cause: Pi (#10504): utils/ansi.ts splitIncompleteAnsiSuffix and core/bash-executor.ts hold back a trailing unfinished OSC/CSI sequence (window of 256 UTF-16 units) until the next chunk, flush it and the streaming decoder at exit (also after an abort), and skip chunks that sanitize to nothing. SplitIncompleteANSISuffix replaces the lookahead with a scanner (RE2 has no lookahead); it agrees with Pi's function on 20000 random strings (one-off differential against node).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b6c65b3c10(2026-10-07) - Tests:
coding/session_bash_split_ansi_upstream_test.go::TestUpstreamBashEscapeSequencesSplitAcrossOutputChunks - Release note:
!command and RPCbashoutput no longer keeps fragments of color codes, such as a straym, when an escape sequence is split across output chunks. PiG holds a trailing unfinished sequence of up to 256 characters until the next chunk or the end of the command.
BF-b6d8ab0
Fix changelog fragment and bug ledger naming the pre-squash commit of #307 (#317)
- Source: PiG's own defect
- Symptom: Fix changelog fragment and bug ledger naming the pre-squash commit of #307 (#317)
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
b6d8ab0bfa(2026-10-24, #317) - Tests:
ai/socket_read_monitor_linux_norace_test.go::TestSocketWriterHelper,ai/socket_read_monitor_linux_norace_test.go::TestStreamingTransportReadDoesNotWakeTheRuntimeMonitor,coding/extension/host/subprocess/conn_close_test.go::TestCloseAfterThePeerClosedSucceeds,coding/extension/host/subprocess/conn_close_test.go::TestCloseReturnsARealTransportCloseFailure,coding/extension/host/subprocess/conn_close_test.go::TestTransportAlreadyClosedClasses,tui/config_selector_oracle_test.go::TestConfigSelectorMatchesPi,tui/terminal_negotiation_stop_test.go::TestTerminalInputCarriesOnlyWhatIsSent,tui/terminal_split_reply_test.go::TestForwardTerminalConsumesAnOwedReplySplitAcrossSlowReads
BF-b730205
OpenRouter image models that only the Images API serves (for example openai/gpt-image-2.5-flare) now…
- Source: A Pi defect that PiG fixed first (divergence D128)
- Symptom: OpenRouter image models that only the Images API serves (for example
openai/gpt-image-2.5-flare) now generate: whenchat/completionsrefuses the model, PiG retries through/images(D128, pi-mono #10652). - Root cause: the refusal is OpenRouter's own statement of which endpoint serves the model; the catalog carries no field that tells the two apart (FLUX and Gemini image models work through chat completions), so the endpoint is discovered from the response.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10652
- Removed when Pi fixes it: Pi sends these models to the Images API.
- Fix commits:
9cc4d406f7(2026-10-10),b73020581b(2026-10-10) - Tests:
ai/openrouter_images_api_endpoint_test.go::TestOpenRouterImagesAPIFallbackIsNarrowAndRunsOnce,ai/openrouter_images_api_endpoint_test.go::TestOpenRouterImagesAPIRequestRunsTheHooksAndJoinsTheTextInput,ai/openrouter_images_api_endpoint_test.go::TestOpenRouterImagesOtherFailuresDoNotUseTheImagesAPI,ai/openrouter_images_api_endpoint_test.go::TestOpenRouterImagesUsesTheImagesAPIWhenChatCompletionsRefusesTheModel - Release note: OpenRouter image models that only the Images API serves (for example
openai/gpt-image-2.5-flare) now generate: whenchat/completionsrefuses the model, PiG retries through/images(D128, pi-mono #10652).
BF-b751326
When an extension in a shared Go, Rust or Python process crashes, the error for each stopped extension now…
- Source: PiG's own defect
- Symptom: The message could only say "packed member connection closed".
- Root cause: A Go extension in a packed Go cell that exits mid-tool-call stops its healthy siblings in the same process. Native packed cells are not restarted automatically; /reload recreates them (docs/runtime-cell-reload.md). The quarantine notice says so ("<names> stopped; /reload restarts them"), but when a member's socket-close handler detaches it before watchPackedProcess sees the process exit, the quarantine finds no members left. The user then sees only "extension "go" disabled: packed member connection closed", and later tool calls fail with "read failed: EOF" without explanation. Observed through pig with the robust-notes Go extension beside a crashing one. The connection-closed notice now…
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
311609427b(2026-10-07),66bb7180b3(2026-10-07),b751326e80(2026-10-09) - Tests:
coding/extension/host/subprocess/liveness_test.go::TestPackedMemberTransportClosureDoesNotProveProcessDeath,coding/extension/host/subprocess/packed_go_test.go::TestHost_KillingPackedProcessNeverClaimsItRemainedAlive,coding/resource_loader_extend_test.go::TestResourceLoaderInterfaceExtendsAndReloads,coding/session_extend_resources_test.go::TestSessionExtendsItsLoaderAfterExtensionReplacement,coding/session_extend_resources_test.go::TestSessionExtendsItsLoaderFromResourcesDiscover,coding/session_extend_resources_test.go::TestSessionWithNoResourcesDoesNotEmitResourcesDiscover - Release note: When an extension in a shared Go, Rust or Python process crashes, the error for each stopped extension now says that
/reloadrestarts it. Before, the message could only say "packed member connection closed".
BF-b78cdae
A YAML parse error on a block scalar used as an implicit key (name: x followed by |description: ...)…
- Source: PiG's own defect
- Symptom: A YAML parse error on a block scalar used as an implicit key (
name: xfollowed by|description: ...) draws the carets of theImplicit keys need to be on a single linemessage as the yaml library does when the scalar's content holds a non-ASCII character: the range ends one UTF-16 unit per character after the key's offset, where it used to count bytes and print one caret or too many. - Root cause: The library reports MULTILINE_IMPLICIT_KEY for a block scalar key at [offset, offset + source.length], where offset is the header and the source is the content that follows, so the range counts the content's UTF-16 units from the header. posOfToken added the content's byte length to the byte offset and converted that, so a multi-byte character in the content moved the end past the line and the message printed one caret, or too many ("a: 1\n|b: x\nÿ: 1" printed 1 caret, the library 5). pos now carries the length in units and prettify adds it to the converted start.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b78cdae340(2026-10-11) - Tests:
internal/yaml12/parse_mutation_test.go::TestParseMatchesTheLibraryOnErrorRangesPastNonASCII - Release note: A YAML parse error on a block scalar used as an implicit key (
name: xfollowed by|description: ...) draws the carets of theImplicit keys need to be on a single linemessage as the yaml library does when the scalar's content holds a non-ASCII character: the range ends one UTF-16 unit per character after the key's offset, where it used to count bytes and print one caret or too many.
BF-b84facb
fix(codingagent,tui): extension editor follows the terminal rows; title and image fallback close only the…
- Source: PiG's own defect
- Symptom: fix(codingagent,tui): extension editor follows the terminal rows; title and image fallback close only the foreground
- Root cause: Pi's ExtensionEditorComponent builds its Editor with the TUI (extension-editor.ts), so the window is max(5, floor(rows*0.3)) lines; Pig fixed it at 5. The title used Accent+Reset (ESC[0m) where theme.fg closes with ESC[39m, and DefaultImageTheme did the same for the image fallback line.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
b84facb7d0(2026-10-10) - Tests:
internal/codingagent/extension_editor_test.go::TestExtensionEditorHandleInputMatchesPi,tui/image_theme_test.go::TestDefaultImageThemeClosesOnlyTheForeground
BF-b88044f
A login flow's text prompt accepts an empty answer, as Pi's does, instead of failing with "<prompt> is…
- Source: PiG's own defect
- Symptom: A login flow's text prompt accepts an empty answer, as Pi's does, instead of failing with "<prompt> is required".
- Root cause: runLoginRegisteredOAuth rejected an empty text answer with "<prompt> is required" unless the prompt set allowEmpty. Pi's showPrompt resolves the submitted text, empty or not (login-dialog.ts:58-66), and showAuthPrompt never enforces allowEmpty (interactive-mode.ts:6278-6300). Since the bridge now reports non-cancel failures as errors, that check failed the flow. The prompt now returns the empty answer.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b88044ff8a(2026-10-09) - Tests:
internal/codingagent/config_oauth_login_cancel_test.go::TestConfigOAuthLoginSelectCancelReturnsToMenu,internal/codingagent/login_prompt_empty_test.go::TestOAuthLoginTextPromptAcceptsAnEmptyAnswer - Release note: A login flow's text prompt accepts an empty answer, as Pi's does, instead of failing with "<prompt> is required".
BF-b8c5de6
Interactive startup no longer reads terminal input while it checks for fd and rg unless one of them is being…
- Source: PiG's own defect
- Symptom: Interactive startup no longer reads terminal input while it checks for fd and rg unless one of them is being downloaded, as in Pi.
- Root cause: Pi runs each ensureTool up to its first await in Promise.all argument order, so a present tool or one offline mode skips settles without reading input. ToolsManager.BeginEnsureTool now runs that prefix on the caller and returns the download when one remains; ensureManagedTools runs the prefixes on the owner loop and serves startup input only while downloads are in flight.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),2d99958e18(2026-10-13),b8c5de64d3(2026-10-13),f12896bcc6(2026-10-13) - Tests:
coding/extension/host/subprocess/runtime_node_sdk_bundle_test.go::TestNodeLibraryBundlesRegenerateExactly,coding/extension/host/subprocess/runtime_node_sdk_bundle_test.go::TestNodeSDKBundleRegeneratesExactly,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,internal/codingagent/interactive_init_state_pty_test.go::TestInitBuildsTheInteractiveStateBeforeTheSessionBindChild,internal/codingagent/startup_submit_test.go::TestColorReplyCompletesTheStartupWaitBeforeTheNextInputand 5 more in docs/bug-ledger.json - Release note: Interactive startup no longer reads terminal input while it checks for fd and rg unless one of them is being downloaded, as in Pi. When both tools are present, or offline mode skips the download, a prompt typed during startup reaches the ordinary submit handler instead of reporting "Startup is still in progress".
BF-b8df7eb
On Windows, a session's private snapshot file is created in the temporary directory instead of the session…
- Source: PiG's own defect
- Symptom: On Windows, a session's private snapshot file is created in the temporary directory instead of the session directory, where it was listed while the session was open.
- Root cause: Product: - A Windows session snapshot is created in the temporary directory. It is a copy there, never a clone, and an open file beside the session file was listed in the session directory (TestAgentSessionRuntimeMatchesPi). - An image fallback links a slash-rooted path without a drive (/tmp/x.png) as Pi does: nodepath.IsAbsolute, not filepath.IsAbs. - An alt-screen frame reads the WezTerm environment only for the Kitty protocol, the one use of it; each Windows environment read allocated (TestAltScreenStreamingFrameAllocationCeiling).
- User impact: Wrong output or ordering
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
b8df7eb579(2026-10-15) - Tests:
coding/agent_session_runtime_oracle_test.go::TestAgentSessionRuntimeMatchesPi,tui/alt_screen_frame_allocs_norace_test.go::TestAltScreenStreamingFrameAllocationCeiling,tui/terminal_split_reply_test.go::TestTerminalInputConsumesAnOwedReplySplitAcrossSlowReads - Release note: On Windows, a session's private snapshot file is created in the temporary directory instead of the session directory, where it was listed while the session was open.
BF-b8df7eb-2
On Windows, an image fallback links a path that starts with a slash but no drive (/tmp/x.png) as Pi does…
- Source: PiG's own defect
- Symptom: On Windows, an image fallback links a path that starts with a slash but no drive (
/tmp/x.png) as Pi does, sincepath.isAbsolutecounts it absolute. - Root cause: Product: - A Windows session snapshot is created in the temporary directory. It is a copy there, never a clone, and an open file beside the session file was listed in the session directory (TestAgentSessionRuntimeMatchesPi). - An image fallback links a slash-rooted path without a drive (/tmp/x.png) as Pi does: nodepath.IsAbsolute, not filepath.IsAbs. - An alt-screen frame reads the WezTerm environment only for the Kitty protocol, the one use of it; each Windows environment read allocated (TestAltScreenStreamingFrameAllocationCeiling).
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
b8df7eb579(2026-10-15) - Tests:
coding/agent_session_runtime_oracle_test.go::TestAgentSessionRuntimeMatchesPi,tui/alt_screen_frame_allocs_norace_test.go::TestAltScreenStreamingFrameAllocationCeiling,tui/terminal_split_reply_test.go::TestTerminalInputConsumesAnOwedReplySplitAcrossSlowReads - Release note: On Windows, an image fallback links a path that starts with a slash but no drive (
/tmp/x.png) as Pi does, sincepath.isAbsolutecounts it absolute.
BF-b8df7eb-3
Alt-screen frames outside the Kitty protocol no longer read the environment for WezTerm on every render…
- Source: PiG's own defect
- Symptom: Alt-screen frames outside the Kitty protocol no longer read the environment for WezTerm on every render, which allocated on Windows.
- Root cause: Product: - A Windows session snapshot is created in the temporary directory. It is a copy there, never a clone, and an open file beside the session file was listed in the session directory (TestAgentSessionRuntimeMatchesPi). - An image fallback links a slash-rooted path without a drive (/tmp/x.png) as Pi does: nodepath.IsAbsolute, not filepath.IsAbs. - An alt-screen frame reads the WezTerm environment only for the Kitty protocol, the one use of it; each Windows environment read allocated (TestAltScreenStreamingFrameAllocationCeiling).
- User impact: Wrong display
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
b8df7eb579(2026-10-15) - Tests:
coding/agent_session_runtime_oracle_test.go::TestAgentSessionRuntimeMatchesPi,tui/alt_screen_frame_allocs_norace_test.go::TestAltScreenStreamingFrameAllocationCeiling,tui/terminal_split_reply_test.go::TestTerminalInputConsumesAnOwedReplySplitAcrossSlowReads - Release note: Alt-screen frames outside the Kitty protocol no longer read the environment for WezTerm on every render, which allocated on Windows.
BF-b99457f
tool_search and codemode tool search match Chinese, Japanese and Korean queries: each such character is a…
- Source: A Pi defect that PiG fixed first (divergence D173)
- Symptom:
tool_searchand codemode tool search match Chinese, Japanese and Korean queries: each such character is a search term, where Pi's tokenizer dropped them (earendil-works/pi#10816; D173). - Root cause: A user who writes tool descriptions or queries in Chinese, Japanese or Korean cannot find their tools by search.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10816
- Removed when Pi fixes it: Pi's tokenizer keeps Chinese, Japanese and Korean characters.
- Fix commits:
b99457fa23(2026-10-11) - Tests:
coding/extension/builtin/toolsearch/toolsearch_cjk_test.go::TestBm25RankerFindsAChineseQueryInChineseToolMetadata,coding/extension/builtin/toolsearch/toolsearch_cjk_test.go::TestTokenizeKeepsCJKCharactersAsTerms,coding/extension/builtin/toolsearch/toolsearch_oracle_test.go::TestToolSearchMatchesPi - Release note:
tool_searchand codemode tool search match Chinese, Japanese and Korean queries: each such character is a search term, where Pi's tokenizer dropped them (earendil-works/pi#10816; D173). Text in other scripts tokenizes as before.
BF-b9db06c
fix(subprocess): keep a user_bash operations object alive while its exec runs
- Source: PiG's own defect
- Symptom: fix(subprocess): keep a user_bash operations object alive while its exec runs
- Root cause: The cleanup that releases the extension's operations handle runs when the object is unreachable, and Exec's last use of the object is the request it sends. A collection during the exec released the handle under the running extension exec, and the cancel test read the release notify where it expected the cancel (red: TestUserBashOperationsStayAliveWhileTheirExecRuns).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
b9db06c809(2026-10-21) - Tests: None recorded.
BF-ba8b6b7
/logout reads stored credentials with Pi's 15-second signal, so an unreadable auth.json reports "Could…
- Source: PiG's own defect
- Symptom:
/logoutreads stored credentials with Pi's 15-second signal, so an unreadableauth.jsonreports "Could not read stored credentials" instead of "No stored credentials to remove". - Root cause: interactive-mode.ts:5836 calls listCredentials({ signal: AbortSignal.timeout(15_000) }); with a signal auth-storage.ts:431-432 readLatestData rethrows a failed reload instead of returning the last snapshot, and /logout shows "Could not read stored credentials" (interactive-mode.ts:6025-6029). PiG listed with context.Background(), took the unsignalled fallback and reported no stored credentials.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ba8b6b7030(2026-10-13) - Tests:
internal/codingagent/logout_provider_order_test.go::TestLogoutProviderOptionsReportAnUnreadableAuthFile - Release note:
/logoutreads stored credentials with Pi's 15-second signal, so an unreadableauth.jsonreports "Could not read stored credentials" instead of "No stored credentials to remove".
BF-ba9c37b
Starting pig no longer waits for the daily cleanup of the extension cache
- Source: PiG's own defect
- Symptom: Starting
pigno longer waits for the daily cleanup of the extension cache. - Root cause: The prune loop checks for cancellation only before each entry. A busy failure that arrived after the cancellation counted as skipped, and the last entry's classification could be cut short with nothing to remove, so the collection returned success and the automatic collector wrote its marker for a run that did not finish. The collection now checks for cancellation before it reports success.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ba9c37b247(2026-10-06, #171) - Tests:
coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleBusyEntriesAreSkippedNotErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledBeforeStartTouchesNothing,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledDuringTheLastEntryReportsTheCancellation,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleEntryLeasedAfterClassificationIsSkippedNotAnError,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleOtherErrorsStayErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleStopsWhenItsContextIsCancelled,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestIsBusyErrorLooksThroughWrappedJoins,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestRemoveTreeContextContinuesPastBusyFileand 1 more in docs/bug-ledger.json - Release note: Starting
pigno longer waits for the daily cleanup of the extension cache. The cleanup now runs in the background after the session is running, stops whenpigexits, and skips entries that are in use or that the file system reports busy (such as NFS.nfs*files) instead of failing and repeating at every start.pig extensions cache prunereports the skipped entries, andpig extensions cache statsshows the last background failure.
BF-bada2af
Session and crash timestamps are read like Date.parse of a JavaScript string: U+00A0 and U+FEFF between…
- Source: PiG's own defect
- Symptom: Session and crash timestamps are read like Date.parse of a JavaScript string: U+00A0 and U+FEFF between tokens are white space, and U+2028 and U+2029 are not, as in V8.
- Root cause: DateParse scanned the UTF-8 bytes of its argument, a rule written for a header ByteString, so a U+00A0 or U+FEFF between date tokens (two bytes) made a timestamp NaN where V8 treats it as one white space unit; session and crash-log timestamps (session-manager.ts:795, crash-log) are full JavaScript strings. DateParse now scans utf16.Encode of the string, DateParseHeader keeps the byte rule for the Last-Modified header (remote_catalog_provider). V8's keyword scan stops only at IsWhiteSpace, so U+2028/U+2029 start a keyword and invalidate the date, and its white space token is one unit.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
bada2af6dc(2026-10-11) - Tests:
ai/js_date_parse_random_oracle_test.go::TestDateParseHeaderMatchesNodeOnRandomBytes,ai/js_date_parse_random_oracle_test.go::TestDateParseMatchesNodeOnRandomStrings - Release note: Session and crash timestamps are read like Date.parse of a JavaScript string: U+00A0 and U+FEFF between tokens are white space, and U+2028 and U+2029 are not, as in V8.
BF-bb324f7
A JavaScript or TypeScript extension loads when its registerCommand options carry a member Pi does not…
- Source: PiG's own defect
- Symptom: A JavaScript or TypeScript extension loads when its
registerCommandoptions carry a member Pi does not define, such asargs. - Root cause: TestRegistrationDeclarationFieldsReachTheRegistry fills each field of ToolDecl, CommandDecl, ShortcutDecl and FlagDecl on its own and requires Host.buildExtension to register something different, so a field the host drops between the wire declaration and the registry fails. It found CommandDecl.Args ("argument hint shown in /help"): no SDK sends it, the host never read it, and Pi's registerCommand has no such option (command registration is name, description, getArgumentCompletions, handler). Deleted per the caller-free rule.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
bb324f78bc(2026-10-09),d3b72472fe(2026-10-09) - Tests:
coding/extension/host/subprocess/command_extra_option_test.go::TestNodeCommandWithAnUnknownOptionLoadsLikePi,coding/extension/host/subprocess/register_declaration_fields_wiring_test.go::TestRegistrationDeclarationFieldsReachTheRegistry - Release note: A JavaScript or TypeScript extension loads when its
registerCommandoptions carry a member Pi does not define, such asargs. Pi ignores such members; PiG forwardedargsto the host, which rejected a value that was not a string, and the extension failed to load.
BF-bb3d661
An extension served in-process now refuses to serve a second connection while it serves one, with the error…
- Source: PiG's own defect
- Symptom: A second serve of the same
sdk.Extensionused to overwrite the first one's state and leave the second host waiting for a registration that never came. - Root cause: An Extension holds one connection, run context, tool lock and event bus. A second RunWithConn while the first ran overwrote that state and left the second host waiting for a register that never came: BenchmarkLoadInProcessProviders reused one Extension across a Host per iteration without waiting for the previous serve to end, and died with 'expected ready message, got shutdown' and then hung. The failure reproduces on origin/main with the same benchmark, so it is not the provider-queue change. The SDK now returns an error for the overlap; the benchmark waits for each serve to end.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
bb3d661967(2026-10-25) - Tests:
extensions/sdk/extension_serve_overlap_test.go::TestExtensionRefusesASecondConcurrentServe - Release note: An extension served in-process now refuses to serve a second connection while it serves one, with the error
this extension is already serving a connection; an Extension serves one connection at a time, so build a new one per connection. A second serve of the samesdk.Extensionused to overwrite the first one's state and leave the second host waiting for a registration that never came.
BF-bb888ae
The /model picker now keeps its model list when the catalog refresh times out, as in Pi
- Source: PiG's own defect
- Symptom: The
/modelpicker now keeps its model list when the catalog refresh times out, as in Pi. - Root cause: model-catalog-refresh.ts races the refresh with the selector's signal, so the 15 second timeout rejects the call: the picker shows the timeout and keeps the list it had. PiG waited for the refresh to return and then reloaded the snapshot, showing models the aborted refresh added, and a refresh that ignored the abort never timed out.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
bb888aeef6(2026-10-13) - Tests:
tui/model_selector_refresh_oracle_test.go::TestModelSelectorRefreshModelsMatchesPi,tui/model_selector_runtime_test.go::TestModelSelectorConstructorKeepsTheListAfterATimedOutRefresh - Release note: The
/modelpicker now keeps its model list when the catalog refresh times out, as in Pi. It showed models the aborted refresh had added, and a refresh that ignored the 15 second timeout kept the picker on "Refreshing model catalogs…" until it ended.
BF-bb9e9d8
fix(extensions): parallel Node runtime linking reports the walk-order first failure under every schedule
- Source: PiG's own defect
- Symptom: fix(extensions): parallel Node runtime linking reports the walk-order first failure under every schedule
- Root cause: When i would also have failed, the reported error was the later one, not the walk-order first one the commit claims. The new TestLinkInWalkOrderRunsEveryIndexBelowTheFirstFailure fails under that rule in 2 of 3 -race runs and passes 5/5 -race runs with this fix (TestNodeRuntimeLinkReportsTheWalkOrderFirstFailure stays green under both). The workers now record the lowest failing index (a CAS minimum) and skip only indices above it, so every index below the lowest failure runs and its error is the first in errs. The loop moves into linkInWalkOrder so the schedule is testable without a filesystem; TestLinkInWalkOrderStopsOnACancelledContext pins the context check.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
bb9e9d83d1(2026-10-11) - Tests:
coding/extension/host/subprocess/node_runtime_cache_test.go::TestLinkInWalkOrderRunsEveryIndexBelowTheFirstFailure,coding/extension/host/subprocess/node_runtime_cache_test.go::TestLinkInWalkOrderStopsOnACancelledContext,coding/extension/host/subprocess/node_runtime_cache_test.go::TestNodeRuntimeLinkReportsTheWalkOrderFirstFailure
BF-bc24321
fix(mem M3): arm the idle collection at run end and run it after 500 ms
- Source: PiG's own defect
- Symptom: fix(mem M3): arm the idle collection at run end and run it after 500 ms
- Root cause: A run's end now arms the D172 idle collection, so a run shorter than one spinner tick collects too. The arm survives wakes and clears when the collection fires, so input restarts the wait instead of dropping it. The delay follows Pi-Bolt's IDLE_GC_DELAY_MS (idle-gc.ts): 500 ms.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
bc243218dd(2026-10-13) - Tests:
internal/codingagent/interactive_layout_idle_test.go::TestIdleHeapCollectionAfterRun
BF-bc4cd62
toPrecision rounds a value just below a power of ten (1e-11 to 16 digits, 9.999999999999998e9 to 16 digits)…
- Source: PiG's own defect
- Symptom:
toPrecisionrounds a value just below a power of ten (1e-11 to 16 digits, 9.999999999999998e9 to 16 digits) to the digits Node prints (9.999999999999999e-12,9999999999.999998) instead of carrying it up to the power of ten. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
bc4cd62404(2026-10-10) - Tests:
internal/jsstring/toprecision_oracle_test.go::TestToPrecisionAgainstNodeCorpus,internal/jsstring/toprecision_oracle_test.go::TestToPrecisionKeepsTheExponentOfAValueJustBelowAPowerOfTen - Release note:
toPrecisionrounds a value just below a power of ten (1e-11 to 16 digits, 9.999999999999998e9 to 16 digits) to the digits Node prints (9.999999999999999e-12,9999999999.999998) instead of carrying it up to the power of ten.
BF-bc8d37f
FileModelsStore roots and index keys: new live-Pi oracle…
- Source: PiG's own defect
- Symptom:
FileModelsStoreroots and index keys: new live-Pi oracleTestFileModelsStoreNonObjectRootsAndIndexKeysMatchPi(ai/models_store_file_oracle_test.go; 900 models-store.json files x 1-5 operations; null, array, number and boolean roots, integer-index provider ids, valid files holding null/number/string/array entries; TypeError text compared). - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),bc8d37f470(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/models_store_file_oracle_test.go::TestFileModelsStoreNonObjectRootsAndIndexKeysMatchPiand 5 more in docs/bug-ledger.json - Release note:
FileModelsStoreroots and index keys: new live-Pi oracleTestFileModelsStoreNonObjectRootsAndIndexKeysMatchPi(ai/models_store_file_oracle_test.go; 900 models-store.json files x 1-5 operations; null, array, number and boolean roots, integer-index provider ids, valid files holding null/number/string/array entries; TypeError text compared). Real fixes: PiG rejected every non-object root where Pi accepts JSON arrays (read as an index-keyed object, written back as an array: a deleted or unset element isnull, an index write extends the array) and numbers/booleans (read as empty, delete rewrites the scalar, write throwsCannot create property 'x' on number '5'), and a null root now throws the per-operation TypeErrors (Cannot read properties of null (reading 'x'),Cannot set properties of null (setting 'x'),Cannot convert undefined or null to object).TestFileModelsStoreRejectsInvalidJSONAndCancelledContextsasserted the old[]-is-corrupt behaviour; it now uses corrupt text. 11 mutations red (+1 untestable: the 2^32-1 array-index bound). Not compared: a stored value that is not a catalog reads back as itself in Pi (PiG's typed entry cannot hold it, so such files are only written and deleted), string-valued roots, V8 JSON parse text.
BF-bcc2046
A nested list whose last item is empty but for spaces after its marker is followed by an empty row before the…
- Source: PiG's own defect
- Symptom: A nested list whose last item is empty but for spaces after its marker is followed by an empty row before the item's next text (
- - \n a). - Root cause: '- - \n a' lexes as list, space ' \n', paragraph in marked, so Pi renders an empty row between the nested bullet and the text; '- -\n a' has a one-character remainder and no row. 9 new rows in TestMarkdownRenderMatchesPi (7 red before).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
bcc20464ec(2026-10-10) - Tests:
tui/markdown_render_oracle_test.go::TestMarkdownRenderMatchesPi - Release note: A nested list whose last item is empty but for spaces after its marker is followed by an empty row before the item's next text (
- - \n a).
BF-bd3e8ab
On arm64 and other CPUs with a fused multiply-add, Kimi, Radius and xAI OAuth credentials store the expires…
- Source: PiG's own defect
- Symptom: The expiry could differ in the last digit and the minutes by one at a half-minute boundary.
- Root cause: Go may fuse xy + z into one multiply-add on arm64, ppc64le, s390x and riscv64, also across statements and through an inlined return; JavaScript rounds every product and return value first. An explicit float64() conversion now rounds: - expectedSavings = continuationProbability missCost - warmCost (cache-warmer.ts:390), which the decision event, /session and the warm threshold use; - the Radius shimmer offset (elapsedMs / 1000) * 10 before index - offset (radius-login-selector.ts:21-24); - smooth()'s return value, textFade and the fly-home arc of the easter egg (easter-egg-3d.ts:266-270, 1007, 1151-1152).
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a00dbe6901(2026-10-13),bd3e8ab24b(2026-10-13) - Tests:
ai/device_code_poll_oracle_test.go::TestDeviceCodePollDeadlineRoundsTheProductLikePi,ai/oauth_expiry_arithmetic_test.go::TestBuiltinOAuthExpiryUsesJavaScriptArithmetic,ai/openai_codex_http_error_test.go::TestCodexHTTPErrorResetMinutesRoundTheProductFirst,internal/codingagent/cache_warmer_test.go::TestCacheWarmingExpectedSavingsRoundsTheProductLikePi,internal/codingagent/pig_egg3d_test.go::TestPig3dFlightHomeOffsetsRoundLikePi,internal/codingagent/radius_login_oracle_test.go::TestRadiusLoginMenuRendersLikePi - Release note: On arm64 and other CPUs with a fused multiply-add, Kimi, Radius and xAI OAuth credentials store the
expirestime Pi computes for a fractionalexpires_in, the device-code login deadline matches Pi's, and the ChatGPT usage-limit message names the same "Try again in ~N min." as Pi: eachx * 1000product is rounded before it is added, as JavaScript does. Before, the expiry could differ in the last digit and the minutes by one at a half-minute boundary.
BF-bd3e8ab-2
On arm64 and other CPUs with a fused multiply-add, the cache-warming expected savings (shown by /session…
- Source: PiG's own defect
- Symptom: They could differ from Pi in the last digit, which changed some shimmer colors.
- Root cause: Go may fuse xy + z into one multiply-add on arm64, ppc64le, s390x and riscv64, also across statements and through an inlined return; JavaScript rounds every product and return value first. An explicit float64() conversion now rounds: - expectedSavings = continuationProbability missCost - warmCost (cache-warmer.ts:390), which the decision event, /session and the warm threshold use; - the Radius shimmer offset (elapsedMs / 1000) * 10 before index - offset (radius-login-selector.ts:21-24); - smooth()'s return value, textFade and the fly-home arc of the easter egg (easter-egg-3d.ts:266-270, 1007, 1151-1152).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
a00dbe6901(2026-10-13),bd3e8ab24b(2026-10-13) - Tests:
ai/device_code_poll_oracle_test.go::TestDeviceCodePollDeadlineRoundsTheProductLikePi,ai/oauth_expiry_arithmetic_test.go::TestBuiltinOAuthExpiryUsesJavaScriptArithmetic,ai/openai_codex_http_error_test.go::TestCodexHTTPErrorResetMinutesRoundTheProductFirst,internal/codingagent/cache_warmer_test.go::TestCacheWarmingExpectedSavingsRoundsTheProductLikePi,internal/codingagent/pig_egg3d_test.go::TestPig3dFlightHomeOffsetsRoundLikePi,internal/codingagent/radius_login_oracle_test.go::TestRadiusLoginMenuRendersLikePi - Release note: On arm64 and other CPUs with a fused multiply-add, the cache-warming expected savings (shown by
/session, passed to extensions and compared with the $0.05 threshold), the "Sign in with Radius" shimmer and the 3D easter egg's blocks flying home match Pi: each product is rounded before it is added, as JavaScript does. Before, they could differ from Pi in the last digit, which changed some shimmer colors.
BF-bdb7ad8
A single server-sent-event line larger than 32 MiB (for example a base64 image chunk) no longer fails the…
- Source: PiG's own defect
- Symptom: A single server-sent-event line larger than 32 MiB (for example a base64 image chunk) no longer fails the stream with
token too longon the providers that use PiG's SSE decoder. - Root cause: An earlier change replaced the invented 32 MiB cap with math.MaxInt, so a server that never ends a line grows the buffer until the process runs out of memory. Pi is bounded: each of its SSE readers holds the line in a JavaScript string, and Node cannot make one longer than buffer.constants.MAX_STRING_LENGTH (2^29-24; the OpenAI SDK LineDecoder throws 'Cannot create a string longer than 0x1fffffe8 characters', string buffers throw 'RangeError: Invalid string length'). sseLineLimit is that bound.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
bdb7ad8292(2026-10-09),c715193fab(2026-10-09) - Tests:
ai/sse_long_line_test.go::TestSSEDecoderAcceptsALineLargerThan32MiB,ai/sse_long_line_test.go::TestSSEDecoderBoundsALine,ai/sse_long_line_test.go::TestSSELineLimitIsNodesMaxStringLength - Release note: A single server-sent-event line larger than 32 MiB (for example a base64 image chunk) no longer fails the stream with
token too longon the providers that use PiG's SSE decoder. A line is now bounded where Pi's runtime bounds it, at Node's maximum string length (512 MiB), so a server that never ends a line cannot exhaust memory (D134).
BF-beeb36a
Running with no home directory now fails the way Pi's package manager does, instead of reading the project's…
- Source: PiG's own defect
- Symptom: With no
HOMEand no entry for your user in the system's user database (a container run as a user its passwd file does not list),pigused to take the home directory to be empty, so the.agents/skillsdirectory of the current project was listed as the user's~/.agents/skills, with user scope and without the project-trust prompt. - Root cause: PiG's PackageManagerHomeDir ported
process.env.HOME || homedir()(package-manager.ts:233-235, the pin's getHomeDir) with a failing branch that returned "" where Node's os.homedir() throwsSystemError [ERR_SYSTEM_ERROR]: A system error occurred: uv_os_homedir returned ENOENT (no such file or directory). Pi's callers are resolvePath (:2213, :2217) and addAutoDiscoveredResources (:2464, the first statement that matters in resolve()), so Pi's resolve() rejects there; PiG's collectors ignore errors and rely on ValidateAutoDiscoveredOverrides, which runs first in startup, reload and the config command (validateConfiguredResourceEntries), to report what Pi's resolve() rejects with. That… - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
beeb36afc7(2026-10-24) - Tests:
coding/cli/no_home_directory_test.go::TestResourceLoadingFailsWhereNoHomeDirectoryExists,coding/cli/no_home_directory_test.go::TestStartupFailsWhereNoHomeDirectoryExists,coding/cli/no_home_directory_test.go::TestStartupFollowsNodeWhenThereIsNoHomeDirectory,coding/cli/package_agents_skills_upstream_test.go::TestPackageManagerHomeDirFallsBackToSystemHome,internal/codingagent/home_dir_test.go::TestNoHomeDirectoryLeavesTheProjectsAgentsSkillsTheProjectsOnly,internal/codingagent/home_dir_test.go::TestPackageManagerHomeDirThrowsWhereNodesHomedirDoes,internal/codingagent/home_dir_test.go::TestValidateAutoDiscoveredOverridesRejectsWhereNoHomeDirectoryExists,internal/nodehome/nodehome_test.go::TestDirIsNodesOSHomedirand 1 more in docs/bug-ledger.json - Release note: Running with no home directory now fails the way Pi's package manager does, instead of reading the project's
.agents/skillsas your own. With noHOMEand no entry for your user in the system's user database (a container run as a user its passwd file does not list),pigused to take the home directory to be empty, so the.agents/skillsdirectory of the current project was listed as the user's~/.agents/skills, with user scope and without the project-trust prompt. Startup in print and RPC mode, a reload andpig confignow stop withA system error occurred: uv_os_homedir returned ENOENT (no such file or directory)and exit status 1, which is what Node'sos.homedir(), and so Pi, reports there. Pi stops even earlier, at load, whatever the mode; PiG, which can run without a home directory by settingPIG_HOME, stops when it loads resources.
BF-bf45320
Go embedding hosts can bind extension appends through coding.Session.AppendExtensionEntry, retaining…
- Source: PiG's own defect
- Symptom: Previously, calling
AppendCustomEntryallocated a new identity instead of retaining the subprocess ID and timestamp because the public Session API lacked a way to pass them through, leaving duplicate entries in the Node session mirror. - Root cause: Previously, calling
AppendCustomEntryallocated a new identity instead of retaining the subprocess ID and timestamp because the public Session API lacked a way to pass them through, leaving duplicate entries in the Node session mirror. - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
bf453203b6(2026-10-23) - Tests:
coding/session_custom_entries_test.go::TestAppendExtensionEntryPreservesIdentityAndEventSemantics,coding/session_custom_entries_test.go::TestAppendExtensionEntryRejectsWithoutChangingTheLog,coding/session_custom_entries_test.go::TestAppendExtensionEntrySharesTheNativeAppendLock,coding/session_custom_entries_test.go::TestAppendExtensionEntryThroughNodeHost - Release note: Go embedding hosts can bind extension appends through
coding.Session.AppendExtensionEntry, retaining subprocess IDs and timestamps through the native Session writer and preservingentry_appendedsemantics. Previously, callingAppendCustomEntryallocated a new identity instead of retaining the subprocess ID and timestamp because the public Session API lacked a way to pass them through, leaving duplicate entries in the Node session mirror.
BF-c1055c4
Package manifest globs match as Node's fs.globSync does in Pi: brace sets and sequences, extglobs such as…
- Source: PiG's own defect
- Symptom: Package manifest globs match as Node's
fs.globSyncdoes in Pi: brace sets and sequences, extglobs such as*.@(md|txt), POSIX classes,.and empty path segments, and case-insensitive wildcard segments on Windows and macOS. - Root cause: 17 fail a test; the other lets the last combine's shortcut ignore dropEmpties, which is equivalent because the values loop already drops those empties. The port still equals Pi's copy on the 200,000-pattern corpus.
- User impact: Behavior differed from Pi
- Platform: Windows, macOS
- Fixed in: 0.5.0
- Fix commits:
4401fa4630(2026-10-09),4cc5ef904b(2026-10-09),6584435bb6(2026-10-09),6e6b9132c2(2026-10-09),926f5d2e63(2026-10-09),aff3d7266f(2026-10-09),d586d71f85(2026-10-09),ef007a4e5e(2026-10-09),6edaafdfe2(2026-10-10),6f8d83861a(2026-10-10),c1055c449e(2026-10-10),fce99d43d7(2026-10-10) - Tests:
coding/cli/cli_package_glob_test.go::TestCLIGitSourceRefreshesOncePerLoad,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceGlobThrowFirstSourceWins,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceGlobThrowFollowsPi,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceInstallFailureStopsStartup,coding/cli/settings_file_url_upstream_test.go::TestNpmPackageManifestGlobThrowRejectsResolution,coding/cli/settings_file_url_upstream_test.go::TestSettingsPatternErrorsFollowPi,coding/cli/startup_init_budget_test.go::TestLazyRegexpPatternsAllCompile,coding/cli/startup_init_budget_test.go::TestLinkedPackagesCompileNoRegexpAtInitand 52 more in docs/bug-ledger.json - Release note: Package manifest globs match as Node's
fs.globSyncdoes in Pi: brace sets and sequences, extglobs such as*.@(md|txt), POSIX classes,.and empty path segments, and case-insensitive wildcard segments on Windows and macOS.
BF-c10ae73
In RPC mode, runs that an extension command's handler sends with sendMessage or sendUserMessage start in…
- Source: PiG's own defect
- Symptom: In RPC mode, runs that an extension command's handler sends with
sendMessageorsendUserMessagestart in the order it sent them, as in Pi, when the handler suspends or returns; before, two such runs could start in either order. - Root cause: A transport report released the starts an extension command held on one goroutine each, so two sends from one handler could start their runs in either order. Pi queues them on its one event loop in send order. Run them in order on one goroutine.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),c10ae7385e(2026-10-13) - Tests:
coding/cli/rpc_command_runs_test.go::TestRPCCommandRunsReleaseHeldStartsInOrder,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-start.toml - Release note: In RPC mode, runs that an extension command's handler sends with
sendMessageorsendUserMessagestart in the order it sent them, as in Pi, when the handler suspends or returns; before, two such runs could start in either order.
BF-c122088
A durable nested call keeps its arguments in the member order the caller wrote them, as Pi does: the stored…
- Source: PiG's own defect
- Symptom: A durable nested call keeps its arguments in the member order the caller wrote them, as Pi does: the stored call and execute intent, the
pi.livenested slot, tool events, validation messages, and code mode's "took a different path" message (tools.name({...})) no longer sort them. - Root cause: codemode earlier() named the call an earlier attempt made with its arguments sorted, where Pi codemode.ts:783 writes JSON.stringify(made.arguments) in the script's member order. The order was lost before that: ExecuteTool takes a Go map, and startNestedCall stored a ToolCall with no member order, so every nested call's stored call, slot, execute intent, validation message and start event listed its arguments sorted. Session's copyTaskRecord also decoded each task record into Go maps, so a staged record lost the order of its input and checkpoint (Pi copyJson keeps it).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
c122088b8c(2026-10-13) - Tests:
ai/validate_arguments_order_test.go::TestValidateToolArgumentsReportsTheArgumentsInTheCallsOrder,durable/codemode/codemode_test.go::TestDurableCodeMode,durable/codemode/models_test.go::TestCodeModeModels,durable/core/driver/harness_nested_slots_core_test.go::TestCoreANestedSlotShowsTheArgumentsTheCallRunsWith,durable/harness/harness_tasks_scheduling_test.go::TestTaskClose,durable/harness/tool_nested_test.go::TestANestedCallKeepsTheCallersArgumentOrder,durable/harness/tool_nested_test.go::TestANestedCallSlotShowsTheArgumentsItRunsWith - Release note: A durable nested call keeps its arguments in the member order the caller wrote them, as Pi does: the stored call and execute intent, the
pi.livenested slot, tool events, validation messages, and code mode's "took a different path" message (tools.name({...})) no longer sort them. Code mode'smodels.*calls no longer store an extraorderargument.
BF-c1ed918
fix(sdk-rs): give the request-start wire test's RequestMsg the batch, child_session, tool_call_index and…
- Source: PiG's own defect
- Symptom: fix(sdk-rs): give the request-start wire test's RequestMsg the batch, child_session, tool_call_index and args_raw fields
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
c1ed91817e(2026-10-21) - Tests: None recorded.
BF-c1fedc2
The durable read tool reads a file that grows while it is read, such as an active log, instead of failing…
- Source: PiG's own defect
- Symptom: The durable
readtool reads a file that grows while it is read, such as an active log, instead of failing with "changed while it was read". - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
c1fedc2790(2026-10-05) - Tests: None recorded.
- Release note: The durable
readtool reads a file that grows while it is read, such as an active log, instead of failing with "changed while it was read".
BF-c1fedc2-2
Bounded durable tool output drops a byte-order mark only at the start of byte output, and keeps a U+FEFF at…
- Source: PiG's own defect
- Symptom: Bounded durable tool output drops a byte-order mark only at the start of byte output, and keeps a U+FEFF at the start of the retained tail.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
c1fedc2790(2026-10-05) - Tests: None recorded.
- Release note: Bounded durable tool output drops a byte-order mark only at the start of byte output, and keeps a U+FEFF at the start of the retained tail.
BF-c225c97
fix(rpc): compaction_start precedes a refused replacement's error; D148 names the extra compaction_end; D141…
- Source: PiG's own defect
- Symptom: fix(rpc): compaction_start precedes a refused replacement's error; D148 names the extra compaction_end; D141 end-to-end evidence
- Root cause: The D148 text names PiG's extra compaction_end(aborted). The request-time abort check keeps Pi's getAuth reason and its comment says why a caller-owned provider is not called after an abort.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
c225c976e5(2026-10-10) - Tests:
coding/cli/rpc_compaction_thinking_only_test.go::TestRPCCompactionSurvivesThinkingOnlyMessagesTheRequestsOmit,coding/cli/rpc_replacement_order_test.go::TestRPCRefusedReplacementLeavesTheCompactionOfItsBatchAlone
BF-c229e1c
A Pi Durable SQLite file written by PiG can be read by Pi, and the reverse, when a task kind, request ID, or…
- Source: PiG's own defect
- Symptom: A lookup by kind, request ID or document address missed rows written by the other implementation.
- Root cause: The SQLite differential read each file back only through the storages, and its kinds, keys and request IDs were plain ASCII. encodeIndexedString and encodeJSON used encoding/json, which escapes <, >, & and U+2028/U+2029; Pi's sqlite/storage.ts uses JSON.stringify, which writes them literally. An indexed lookup compares the stored text, so scanTasks by kind, submissionByRequest, findDocument and scanDocuments by kind missed rows the other implementation wrote. This happened in both directions: Pi read Pig's file and Pig read Pi's. Both now use jsstring.MarshalJSON. The test now dumps each file through node:sqlite right after the writes and compares the schema and every row. That exposed a…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
97e178f7f6(2026-10-09),c229e1cae8(2026-10-10) - Tests:
ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,durable/codecs_test.go::TestDocumentRecordMarshalsInPiStorageOrder,durable/storage/memory_pi_oracle_test.go::TestMemoryStorageMatchesPiOnSeededPrograms,durable/storage/sqlite_pi_oracle_test.go::TestSqliteStorageMatchesPiOnSeededPrograms - Release note: A Pi Durable SQLite file written by PiG can be read by Pi, and the reverse, when a task kind, request ID, or document kind or key contains
<,>,&, U+2028 or U+2029. PiG now stores indexed columns and records asJSON.stringifywrites them. Before, a lookup by kind, request ID or document address missed rows written by the other implementation. Stored document records listcreatedAtandretiredAtafter the other members, as Pi stores them.
BF-c2b2b8b
fix(tui): the inline memo is used only with tui's own style functions
- Source: PiG's own defect
- Symptom: fix(tui): the inline memo is used only with tui's own style functions
- Root cause: Tests: - TestStreamedMarkdownWithAForeignThemeCallsItsStylesAgain and TestStreamedMarkdownWithAForeignTextColorCallsItAgain use style functions that read a changing generation. - TestStreamedMarkdownFollowsTheDefaultStyle changes one input per step. - TestStreamedMarkdownEqualsAFreshRender now streams with the default theme. Its reference reuses nothing, because one of its styles is wrapped, so a key without the lexer state fails it. - Mutants red: no purity check, no style flags, no default italic, a foreign Color allowed, no context comparison, no lexer state in the key.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
c2b2b8b860(2026-10-10) - Tests:
tui/markdown_streaming_reuse_test.go::TestStreamedMarkdownEqualsAFreshRender,tui/markdown_streaming_reuse_test.go::TestStreamedMarkdownFollowsTheDefaultStyle,tui/markdown_streaming_reuse_test.go::TestStreamedMarkdownRendersOnlyChangedInlineText,tui/markdown_streaming_reuse_test.go::TestStreamedMarkdownWithAForeignTextColorCallsItAgain,tui/markdown_streaming_reuse_test.go::TestStreamedMarkdownWithAForeignThemeCallsItsStylesAgain
BF-c350117
Fixed incorrect MCP reconnect behavior after cancellation during transport cleanup
- Source: PiG's own defect
- Symptom: Previously, reconnect still started a replacement connection after the caller had canceled.
- Root cause: Reconnect did not check caller cancellation after closing the previous client, so it could still start a replacement connection.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
c3501172ab(2026-10-23) - Tests:
coding/mcpext/reconnect_cancel_test.go::TestMCPReconnectDoesNotOpenAfterCancellationDuringCleanup - Release note: Fixed incorrect MCP reconnect behavior after cancellation during transport cleanup. Previously, reconnect still started a replacement connection after the caller had canceled. Reconnect did not check caller cancellation after closing the previous client, so it could still start a replacement connection. Reconnect now completes cleanup and returns the original cancellation cause before starting a replacement.
BF-c3a6eca
Markdown rendering follows Pi for lines made only of Unicode spaces (U+00A0, U+3000, U+2003 and the others…
- Source: PiG's own defect
- Symptom: Markdown rendering follows Pi for lines made only of Unicode spaces (U+00A0, U+3000, U+2003 and the others JavaScript's trim removes) in and after lists, a list item whose first line is one such space, a heading line holding U+2028 or U+2029, a table whose last line is such a space at a narrow width, and a
$$or\[block whose text is only whitespace. - Root cause: Go's strings.TrimSpace strips U+0085 and keeps U+FEFF; JavaScript's trim() does the opposite, and marked's blank line, /^[ \t]*$/, is neither: a line of U+00A0, U+3000, U+2003 or U+2028 is text. Pig used TrimSpace for Pi's
text.trim() === ""and for blank lines: Text rendered a BOM-only string as a row where Pi renders nothing, an assistant text block of U+FEFF was shown, the model selector took<U+0085>das a prefix of "default", and a full-width space line (common between CJK paragraphs) ended a paragraph, a code block, a table or a heading's spacing where Pi keeps it as text. - User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
c3a6eca380(2026-10-11) - Tests:
tui/assistant_message_render_oracle_test.go::TestAssistantMessageComponentRenderMatchesPi,tui/basic_components_oracle_test.go::TestBasicComponentsRenderLikePi,tui/markdown_unicode_space_oracle_test.go::TestMarkdownUnicodeSpaceLinesMatchPi,tui/model_selector_oracle_test.go::TestModelSelectorInputMatchesPi,tui/user_message_render_oracle_test.go::TestUserMessageComponentRenderMatchesPi - Release note: Markdown rendering follows Pi for lines made only of Unicode spaces (U+00A0, U+3000, U+2003 and the others JavaScript's trim removes) in and after lists, a list item whose first line is one such space, a heading line holding U+2028 or U+2029, a table whose last line is such a space at a narrow width, and a
$$or\[block whose text is only whitespace.
BF-c416945
fix(ai): tool-argument member order keeps a key with an escaped lone surrogate
- Source: PiG's own defect
- Symptom: fix(ai): tool-argument member order keeps a key with an escaped lone surrogate
- Root cause: readSchemaObjectOrder read keys through encoding/json, which turns an escaped lone surrogate into U+FFFD, while the decoded object (extensions/sdk/json) keeps the surrogate as JSON.parse does; the order named a key the object did not have, so ArgumentsJSON wrote that member last. The key is now decoded from its literal with the faithful decoder.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
c416945cd5(2026-10-10) - Tests: None recorded.
BF-c47bafd
A fused Go extension's built-in tool execute without an extension context (`createBashTool(cwd).execute(id…
- Source: PiG's own defect
- Symptom: A fused Go extension's built-in tool
executewithout an extension context (createBashTool(cwd).execute(id, params)and the othercreate<Tool>Toolforms) no longer fails with "extension stopped while waiting for builtinTool.execute response" when another extension in the same process stops while the tool runs. - Root cause: The sibling of the createLocalBashOperations().exec fix: createXTool(cwd, options).execute(id, params) without an extension context sent builtinTool.execute over runningExtension()'s connection, the first running extension in the process, so it ended with that extension's shutdown ("extension stopped while waiting for builtinTool.execute response").
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.3
- Fix commits:
c47bafd99d(2026-10-22) - Tests:
test/extension-conformance/builtin_local_exec_fused_test.go::TestConformance_FusedBuiltinToolWithoutContextIsNotCarriedByAnotherExtension,test/extension-conformance/builtin_local_exec_fused_test.go::TestConformance_FusedLocalExecIsNotCarriedByAnotherExtension,test/extension-conformance/builtin_tool_delegate_test.go::TestConformance_BuiltinToolDelegationAcrossSDKs,test/extension-conformance/builtin_tool_delegate_test.go::TestConformance_BuiltinToolOverrideDelegatesAcrossSDKs - Release note: A fused Go extension's built-in tool
executewithout an extension context (createBashTool(cwd).execute(id, params)and the othercreate<Tool>Toolforms) no longer fails with "extension stopped while waiting for builtinTool.execute response" when another extension in the same process stops while the tool runs. The call has noContext, so it was sent over the connection of the first running extension in the process. The host now runs the tool for a fused extension directly, reaching back into the extension in the same process for the functions of its options and its streamed updates.
BF-c4ad739
Extension shortcut conflict warnings name both extensions ("registered by both A and B
- Source: PiG's own defect
- Symptom: Extension shortcut conflict warnings name both extensions ("registered by both A and B.
- Root cause: Pi's buildGroups uses a Map (insertion order) and a stable sort; Pig collected groups from a Go map, so groups tying on origin, scope and source (auto resources from different baseDirs) came out in random order.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6d9b403c0a(2026-10-11),c4ad739eb8(2026-10-11) - Tests:
coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,tui/config_selector_test.go::TestBuildResourceGroupsTiesKeepFirstSeenOrder - Release note: Extension shortcut conflict warnings name both extensions ("registered by both A and B. Using B."), and shortcut keys are lower-cased with JavaScript rules so
İno longer collides withi(coding/extension/host/inproc/runner.goShortcuts, runner.ts:673-716; live-Pi oracleTestRunnerShortcutsMatchPi, 800 probes; mutations on the message, both ToLower sites and the reserved-wins rule fail it).
BF-c4d51d5
The OpenAI Codex login reads the account id from the access token the way Pi does: the token payload is…
- Source: PiG's own defect
- Symptom: The OpenAI Codex login reads the account id from the access token the way Pi does: the token payload is decoded as standard base64 with Pi's padding and whitespace rules (a payload using the URL-safe
-or_characters is not accepted), and the decoded bytes are read as Latin-1 characters, so a non-ASCII account id comes out as Pi's text. - Root cause: A differential test runs Pi's private decodeJwt and getAccountId in Node (testdata/codexaccount_id.mjs cuts them out of the pinned source) over 583 tokens and found 78 differences. Pi decodes the payload with atob(): the standard alphabet only, forgiving padding and ASCII whitespace, and one character per decoded byte, then JSON.parse. Pig used base64url (raw, then padded): it refused a payload with whitespace Pi reads, accepted '-' and '' that make atob throw, and read a UTF-8 account id as UTF-8 where Pi reads its bytes as Latin-1. jsAtob now implements the WHATWG forgiving-base64 decode. The length-1-mod-4 and alphabet checks were equivalent mutants (the unpadded standard decoder…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2c04c1445a(2026-10-09),c4d51d5c1a(2026-10-09) - Tests:
ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,ai/codex_account_id_oracle_test.go::TestCodexAccountIDMatchesPi - Release note: The OpenAI Codex login reads the account id from the access token the way Pi does: the token payload is decoded as standard base64 with Pi's padding and whitespace rules (a payload using the URL-safe
-or_characters is not accepted), and the decoded bytes are read as Latin-1 characters, so a non-ASCII account id comes out as Pi's text.
BF-c4fae28
Provider request bodies write a lone UTF-16 surrogate as its escape, as JSON.stringify does, instead of…
- Source: PiG's own defect
- Symptom: Provider request bodies write a lone UTF-16 surrogate as its escape, as
JSON.stringifydoes, instead of U+FFFD for each byte of its Go form. - Root cause: marshalRequestBody used encoding/json, which writes U+FFFD for each of the three WTF-8 bytes a Go string holds for a lone UTF-16 surrogate, where JSON.stringify writes the unit's escape. Pi leaves some strings unsanitized (the Codex instructions, openai-codex-responses.ts:552; an assistant message's reasoning fields), so PiG sanitized the first and corrupted the second. A body with a U+FFFD character is now encoded with the JSON.stringify encoder (jsstring.MarshalJSON), also inside the request types' own MarshalJSON; any other body encodes as before, so the request allocation gates are unchanged.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
c4fae282a7(2026-10-11) - Tests:
ai/message_text_surrogate_test.go::TestMessageTextSanitizesLoneSurrogatesOnTheWire,ai/openai_message_marshal_test.go::TestOpenAIMessageEncodesAsItsMarshalJSON,ai/request_body_lone_surrogate_test.go::TestRequestBodyWritesALoneSurrogateAsItsEscape - Release note: Provider request bodies write a lone UTF-16 surrogate as its escape, as
JSON.stringifydoes, instead of U+FFFD for each byte of its Go form. It matters for the strings Pi does not sanitize: the Codexinstructions, now sent as Pi sends them, and the reasoning fields of an assistant message. A body with no U+FFFD character encodes as before.
BF-c4fef6c
Streaming long code blocks no longer grows memory with every frame: the syntax-highlight cache keeps one copy…
- Source: PiG's own defect
- Symptom: Streaming long code blocks no longer grows memory with every frame: the syntax-highlight cache keeps one copy of a streamed block and is bounded in bytes.
- Root cause: The main screen asks the root for the lines kept since the revision it drew last. Line resets and the differential scan start at the first line that can differ: the earlier of the kept prefix and both frames' cursor rows. The per-line reuse of earlier reset results stays, but only while the previous input array is intact: it was composed with overlays, or the root has rendered at most once since. Overlay frames keep nothing, and a frame after RestoreRenderState scans everything. Mouse dispatch reads child heights from the window.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
10aebd5c05(2026-10-09),136e3703be(2026-10-10),79eadcf623(2026-10-10),ab5be97bbe(2026-10-10),c4fef6c21a(2026-10-10) - Tests:
ai/complexity_scaling_norace_test.go::TestScalingProviderRequestPerTurn,ai/complexity_scaling_norace_test.go::TestScalingProviderStreamPerDelta,coding/cli/complexity_scaling_norace_test.go::TestScalingExtensionEventStatePush,coding/complexity_scaling_norace_test.go::TestScalingSessionTurn,coding/extension/host/subprocess/complexity_scaling_norace_test.go::TestScalingMessageUpdatePayload,internal/codingagent/compaction/complexity_scaling_norace_test.go::TestScalingCompactionAndContextEstimate,internal/codingagent/complexity_scaling_norace_test.go::TestScalingSessionLogOperations,internal/codingagent/tools/complexity_scaling_norace_test.go::TestScalingToolsand 11 more in docs/bug-ledger.json - Release note: Streaming long code blocks no longer grows memory with every frame: the syntax-highlight cache keeps one copy of a streamed block and is bounded in bytes.
BF-c51c77e
An extension that calls registerTool({}) or registerTool(null) now fails to load with Pi's error text…
- Source: PiG's own defect
- Symptom: An extension that calls
registerTool({})orregisterTool(null)now fails to load with Pi's error text (the missing parameter schema, or the TypeError of readingparameters), not a PiG-only message. - Root cause: The Node runtime threw its own 'registerTool requires a definition with name' before Pi's checks. Pi's loader.ts:289-295 reads tool.parameters first, so {} fails the object-schema check (Tool "undefined" ...) and null throws the TypeError of the property read.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
c51c77e0b5(2026-10-11) - Tests:
coding/extension/host/subprocess/upstream_tool_schema_test.go::TestRegisterToolWithoutANameFailsLikePisLoader - Release note: An extension that calls
registerTool({})orregisterTool(null)now fails to load with Pi's error text (the missing parameter schema, or the TypeError of readingparameters), not a PiG-only message.
BF-c58ceed
chord/host.Forbidden("") keeps the empty message, as Pi's forbidden("") does; Forbidden() still says…
- Source: PiG's own defect
- Symptom:
chord/host.Forbidden("")keeps the empty message, as Pi'sforbidden("")does;Forbidden()still says "forbidden". - Root cause: New package chord/host (the runtime of Chord's root export; the shared base stays in chord so the durable core links none of it) with packages/chord/src/schema.ts (canonicalJson with UTF-16 key order, hash64, deepEqual, assertValid, normalize, canonicalIdentity, fits), contract.ts (method, opaque, remote, local, coverage, bind, descriptions), errors.ts (ChordError, isChordError, forbidden, invalid) and wire.ts (the Frame union as a sealed interface, FrameSchema, isFrame, toWireError, fromWireError).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
c58ceeda89(2026-10-12),fa905766d4(2026-10-12) - Tests:
ai/validate_formats_oracle_test.go::TestTypeboxFormatsMatchPiOnSeededStrings,chord/host/boundary_upstream_test.go::TestPackageBoundaryDoesNotDependOnPiPackagesOrFilesOutsideChord,chord/host/contract_upstream_test.go::TestContractsBindOtherVersionsOnlyThroughAcceptsAdapters,chord/host/contract_upstream_test.go::TestContractsCompareOpaqueMembersByNameVersionAndFunctionMembersStructurally,chord/host/contract_upstream_test.go::TestContractsCoverAdditiveChangesAndRefuseBreakingOnes,chord/host/contract_upstream_test.go::TestContractsFingerprintsAreStableAndDescriptionBased,chord/host/contract_upstream_test.go::TestContractsNormalizeInputAndOmitDefaultsFromTheIdentity,chord/host/contract_upstream_test.go::TestContractsRejectReservedMemberNamesAndOpaqueRemoteMembersand 12 more in docs/bug-ledger.json - Release note:
chord/host.Forbidden("")keeps the empty message, as Pi'sforbidden("")does;Forbidden()still says "forbidden".IsChordErrortakes the code as an optional argument and, likeToWireError, no longer looks through wrappers, so an aggregate of ChordErrors goes on the wire asunavailable, as in Pi.ChordError.Nameis "ChordError".
BF-c5ce588
Gemini 4 and later models send thinkingLevel instead of thinkingBudget, fixing Gemini 4 Argon on Vertex…
- Source: PiG's own defect
- Symptom: Gemini 4 and later models send
thinkingLevelinstead ofthinkingBudget, fixing Gemini 4 Argon on Vertex and the ignored thinking level on the Gemini API. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
c5ce588c58(2026-10-14, #10890) - Tests:
ai/google_thinking_level_map_upstream_test.go::TestGoogleThinkingLevelMapRegression - Release note: Gemini 4 and later models send
thinkingLevelinstead ofthinkingBudget, fixing Gemini 4 Argon on Vertex and the ignored thinking level on the Gemini API. Carried forward from Pi 1692bba6 (earendil-works/pi#10890), past the 1.2.0+3daa2028 pin.
BF-c625bb4
The durable powershell tool fails with "No command to run" when its program list is empty, instead of…
- Source: PiG's own defect
- Symptom: The durable
powershelltool fails with "No command to run" when its program list is empty, instead of reporting success. - Root cause: Pi's runCommand throws "No command to run" when no command form ran, which createPowerShellTool({ programs: [] }) reaches (packages/durable/src/tools/bash.ts runCommand). PiG returned success without running anything.
- User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
c625bb4536(2026-10-05) - Tests:
durable/tools/powershell_upstream_test.go::TestPowerShellFailsWithoutAProgramToTry - Release note: The durable
powershelltool fails with "No command to run" when its program list is empty, instead of reporting success.
BF-c6c2cf3
An env request whose context is already cancelled is now always sent to the daemon and then cancelled, as…
- Source: PiG's own defect
- Symptom: An
envrequest whose context is already cancelled is now always sent to the daemon and then cancelled, as in Pi, instead of failing about half the time with a localabortederror before it was sent. - Root cause: env/daemon: an overflow during open (a switch in the first sync, e.g. ENOSPC) went out as {"kind":"change","overflow":true} without the mode, because the watcher was not stored yet; watch.rs:583 always sends "mode":"polling" with it, since only switch_to_polling reports overflow. The event mapping is watchEvent; TestWatchEventsMatchThe RustDaemon pins the three event shapes (not red-proven: the function is new; the pre-fix failure is the missing mode during open, and dropping the mode kills the test).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1f5a782c0a(2026-10-08),5dd595fb87(2026-10-08),beefcd709e(2026-10-09),f68aa400e5(2026-10-09),ffe6a53a07(2026-10-09),c6c2cf3239(2026-10-10) - Tests:
chord/delta/apply_immutable_upstream_test.go::TestCheckedImmutableOperationApplication,chord/delta/delta_upstream_test.go::TestDeltaCanonicalStrings,chord/delta/delta_upstream_test.go::TestDeltaPathAndPrototypeSafety,chord/delta/json_kind_message_test.go::TestCloneJSONReportsPisMessageForEachGoKind,chord/delta/normalize_error_order_test.go::TestDiffCandidateReportsTheFirstInvalidPropertyDeterministically,chord/delta/retention_upstream_test.go::TestDeltaTrackerRetentionAcrossGCJobs,chord/delta/retention_upstream_test.go::TestTrackerReleasesADroppedOpenChange,chord/delta/tracker_delta_lifecycle_upstream_test.go::TestDeltaImmutableTrackerLifecycleand 8 more in docs/bug-ledger.json - Release note: An
envrequest whose context is already cancelled is now always sent to the daemon and then cancelled, as in Pi, instead of failing about half the time with a localabortederror before it was sent. A request cancelled while the daemon is starting now waits for the start, as in Pi.
BF-c7ebb23
A cancelled prompt in a config.oauth login now rejects with Pi's Login cancelled in every SDK (it was…
- Source: PiG's own defect
- Symptom: A cancelled prompt in a
config.oauthlogin now rejects with Pi'sLogin cancelledin every SDK (it wasoauth prompt cancelled, and Node'sonSelectresolvedundefined), so/loginreturns to the menu instead of reporting a failed login. - Root cause:
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
nativeExtensionProviders.get(id) ?? builtins.get(id)); registryAuthConfig now does the same, so the methods come from the retained provider object on every register, refresh and publication path; - the host carrier did not map auth.apiKey.login, auth.oauth.login or loginLabel. It now does, and their prompt and notify reverse calls decode into the closed AuthPrompt and AuthEvent unions (auth/types.ts); any other type fails the login with an…
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
08bf0761c9(2026-10-09, #201),26d302a2d7(2026-10-09, #201),c7ebb23d87(2026-10-09) - Tests:
coding/extension/host/subprocess/native_provider_login_test.go::TestDecodeNativeLoginCallback,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeAuthInteractionAnswersTheLoginDialog,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeProviderCarrierCarriesDeclaredLogins,coding/extension/host/subprocess/native_provider_oauth_test.go::TestNativeProviderOAuthLoginCallbacks,coding/extension/host/subprocess/oauth_bridge_test.go::TestOAuthCallbackInitiationFollowsRealCallbackEntry,coding/extension/host/subprocess/oauth_legacy_callbacks_test.go::TestOAuthProxy_LoginRelaysTheLegacyCallbackSurface,coding/extension/host/subprocess/oauth_legacy_callbacks_test.go::TestOAuthProxy_ManualCodeInputShowsPisPromptMessage,internal/codingagent/config_oauth_login_cancel_test.go::TestConfigOAuthLoginSelectCancelReturnsToMenuand 6 more in docs/bug-ledger.json - Release note: A cancelled prompt in a
config.oauthlogin now rejects with Pi'sLogin cancelledin every SDK (it wasoauth prompt cancelled, and Node'sonSelectresolvedundefined), so/loginreturns to the menu instead of reporting a failed login.
BF-c7ebb23-2
A config.oauth login's onManualCodeInput asks "Paste the authorization code", as Pi does, and a provider…
- Source: PiG's own defect
- Symptom: A
config.oauthlogin'sonManualCodeInputasks "Paste the authorization code", as Pi does, and a provider object'smanual_codeprompt shows its own message and placeholder. - Root cause:
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
nativeExtensionProviders.get(id) ?? builtins.get(id)); registryAuthConfig now does the same, so the methods come from the retained provider object on every register, refresh and publication path; - the host carrier did not map auth.apiKey.login, auth.oauth.login or loginLabel. It now does, and their prompt and notify reverse calls decode into the closed AuthPrompt and AuthEvent unions (auth/types.ts); any other type fails the login with an…
- the registry composed the provider's auth from the built-in catalog, not from the object, so its OAuth method, loginLabel and own API-key login were never offered. Pi's composeProvider takes the native provider as the base (model-runtime.ts:301-303
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
08bf0761c9(2026-10-09, #201),26d302a2d7(2026-10-09, #201),c7ebb23d87(2026-10-09) - Tests:
coding/extension/host/subprocess/native_provider_login_test.go::TestDecodeNativeLoginCallback,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeAuthInteractionAnswersTheLoginDialog,coding/extension/host/subprocess/native_provider_login_test.go::TestNativeProviderCarrierCarriesDeclaredLogins,coding/extension/host/subprocess/native_provider_oauth_test.go::TestNativeProviderOAuthLoginCallbacks,coding/extension/host/subprocess/oauth_bridge_test.go::TestOAuthCallbackInitiationFollowsRealCallbackEntry,coding/extension/host/subprocess/oauth_legacy_callbacks_test.go::TestOAuthProxy_LoginRelaysTheLegacyCallbackSurface,coding/extension/host/subprocess/oauth_legacy_callbacks_test.go::TestOAuthProxy_ManualCodeInputShowsPisPromptMessage,internal/codingagent/config_oauth_login_cancel_test.go::TestConfigOAuthLoginSelectCancelReturnsToMenuand 6 more in docs/bug-ledger.json - Release note: A
config.oauthlogin'sonManualCodeInputasks "Paste the authorization code", as Pi does, and a provider object'smanual_codeprompt shows its own message and placeholder.
BF-c8a90a6
Pressing the login dialog's cancel key again cancels again, and Enter after a cancelled prompt leaves the…
- Source: PiG's own defect
- Symptom: Pressing the login dialog's cancel key again cancels again, and Enter after a cancelled prompt leaves the field unchanged, as in Pi.
- Root cause: (1) login-dialog.ts handleInput has no done guard: a second cancel key aborts and calls onComplete again; Pig ignored every key once cancelled. (2) The Input's onSubmit acts only while an input resolver is pending (replaceInputWithSubmittedText + resolve); after cancel Pig still replaced the field with '> text'. HandleInput drops the done guard and OnSubmit returns when no input is pending.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
c8a90a61db(2026-10-07) - Tests:
tui/login_dialog_oracle_test.go::TestLoginDialogInputMatchesPi - Release note: Pressing the login dialog's cancel key again cancels again, and Enter after a cancelled prompt leaves the field unchanged, as in Pi.
BF-c8e3d86
Settings submenus: the type-to-filter box no longer shows a hardware cursor marker (Pi never focuses it), and…
- Source: PiG's own defect
- Symptom: Settings submenus: the type-to-filter box no longer shows a hardware cursor marker (Pi never focuses it), and the highlight callback (theme preview) now fires on every up/down like Pi's SelectList, including when the highlight stays on the same item.
- Root cause: The assignment is removed. SelectSubmenuComponent now has OnSelectionChange (set by newSelectSubmenuStep, its production caller) fired after up/down. Frames compare as text (Pi bold/fg closers vs Pig SGR 0 differ, open renderer question). Overlapping rebinds (a key shared by cancel and up) are not probed: FilterableList checks cancel before up unlike Pi's SelectList; follow-up.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d1f258f335(2026-10-06),56a0400b9c(2026-10-07),c8e3d86378(2026-10-07),ccaade27af(2026-10-07),f2a2b3409b(2026-10-07) - Tests:
internal/codingagent/settings_submenu_oracle_test.go::TestSettingsSubmenusMatchPi,internal/codingagent/thinking_selector_oracle_test.go::TestThinkingSelectorInputMatchesPi,internal/codingagent/tree_row_format_test.go::TestFormatTreeRow_AllEntryTypes,internal/codingagent/tree_selector_oracle_test.go::TestTreeSelectorInputMatchesPi,test/parity/scenarios/settings/06-thinking-selector-search-editing.toml,test/parity/scenarios/settings/08-settings-search-editing.toml,tui/editor_bindings_oracle_test.go::TestEditorRebindingsMatchPi,tui/input_oracle_test.go::TestInputHandleInputMatchesPiand 10 more in docs/bug-ledger.json - Release note: Settings submenus: the type-to-filter box no longer shows a hardware cursor marker (Pi never focuses it), and the highlight callback (theme preview) now fires on every up/down like Pi's SelectList, including when the highlight stays on the same item.
BF-c98e9f9
Without HOME set, the project trust check takes the home directory from the user database, as Pi's…
- Source: PiG's own defect
- Symptom: Without
HOMEset, the project trust check takes the home directory from the user database, as Pi'sos.homedir()does, instead of treating the working directory as the home directory. - Root cause: hasTrustRequiringProjectResources canonicalizes process.env.HOME || homedir() (trust-manager.ts:187), and Node's os.homedir() reads the user database when HOME is unset or empty. PiG fell back to os.UserHomeDir, which reads only HOME, and resolved the empty result against the working directory: the working directory then counted as the user's home, its .agents/skills never required project trust, and every trust check realpathed the working directory twice.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
02c405c179(2026-10-13),c98e9f9bbd(2026-10-13) - Tests:
coding/cli/path_resolve_ports_test.go::TestPathPortsReportAnUnreadableWorkingDirectory,coding/cli/path_resolve_ports_test.go::TestSamePathAndAncestorSkillDirsResolveLikeNode,internal/codingagent/trust_manager_test.go::TestHasTrustRequiringProjectResourcesWithoutHOMEStillChecksTheWorkingDirectory - Release note: Without
HOMEset, the project trust check takes the home directory from the user database, as Pi'sos.homedir()does, instead of treating the working directory as the home directory. A working directory's.agents/skillsnow requires project trust in that case too.
BF-c9acbea
A Session listener that queues more messages than the Session's event buffer holds (64 by default), while it…
- Source: PiG's own defect
- Symptom: A Session listener that queues more messages than the Session's event buffer holds (64 by default), while it runs on the goroutine that emitted its event, publishes its
queue_updateevents in order. - Root cause: The Session's event goroutine waited for execMu, a sync.Mutex, while a producer ran a callback inline under it. testing/synctest does not count a Mutex waiter as durably blocked, so synctest.Wait never returned while the callback was held: TestSessionObservationCloseJoinsClaimedCallback hung on its first run (the executor's shutdown waited for execMu), and TestCompactionFailureWaitsForEndListeners needed a synctest.Wait before compaction to dodge the same wait. execMu is now a lock whose waiters block on a channel receive; taking a free lock is one atomic add. The compaction test's workaround is removed.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
375de4a98c(2026-10-12),c9acbead45(2026-10-13) - Tests:
coding/cli/strip_providers_binary_test.go::TestStripBedrockConverseStreamBuildOmitsAWSSDK,coding/session_compaction_event_order_test.go::TestCompactionFailureWaitsForEndListeners,coding/session_queue_publication_test.go::TestSessionMessageSubscriberQueueBurstPreservesPublicationOrder,coding/session_queue_publication_test.go::TestSessionQueuePublicationOverflowIsReleasedOnClose,coding/session_rpc33_observation_test.go::TestSessionObservationCloseJoinsClaimedCallback,coding/session_rpc33_observation_test.go::TestSessionObservationReservationCanceledBeforeAdmission,third_party/awssdk/internal/endpoints/v2/lazy_test.go::TestLazyRegexpMatchesLikeRegexp - Release note: A Session listener that queues more messages than the Session's event buffer holds (64 by default), while it runs on the goroutine that emitted its event, publishes its
queue_updateevents in order. An update could reachEventsahead of the one published just before it, so a subscriber saw a queue with one message too many. Every event published after such a burst now runs after it.
BF-c9acbea-2
A goroutine that waits to run Session events, such as the Session's event goroutine while a producer runs a…
- Source: PiG's own defect
- Symptom: A goroutine that waits to run Session events, such as the Session's event goroutine while a producer runs a listener itself, now blocks on a channel receive instead of a
sync.Mutex, sotesting/synctestcounts it as durably blocked. - Root cause: The Session's event goroutine waited for execMu, a sync.Mutex, while a producer ran a callback inline under it. testing/synctest does not count a Mutex waiter as durably blocked, so synctest.Wait never returned while the callback was held: TestSessionObservationCloseJoinsClaimedCallback hung on its first run (the executor's shutdown waited for execMu), and TestCompactionFailureWaitsForEndListeners needed a synctest.Wait before compaction to dodge the same wait. execMu is now a lock whose waiters block on a channel receive; taking a free lock is one atomic add. The compaction test's workaround is removed.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
375de4a98c(2026-10-12),c9acbead45(2026-10-13) - Tests:
coding/cli/strip_providers_binary_test.go::TestStripBedrockConverseStreamBuildOmitsAWSSDK,coding/session_compaction_event_order_test.go::TestCompactionFailureWaitsForEndListeners,coding/session_queue_publication_test.go::TestSessionMessageSubscriberQueueBurstPreservesPublicationOrder,coding/session_queue_publication_test.go::TestSessionQueuePublicationOverflowIsReleasedOnClose,coding/session_rpc33_observation_test.go::TestSessionObservationCloseJoinsClaimedCallback,coding/session_rpc33_observation_test.go::TestSessionObservationReservationCanceledBeforeAdmission,third_party/awssdk/internal/endpoints/v2/lazy_test.go::TestLazyRegexpMatchesLikeRegexp - Release note: A goroutine that waits to run Session events, such as the Session's event goroutine while a producer runs a listener itself, now blocks on a channel receive instead of a
sync.Mutex, sotesting/synctestcounts it as durably blocked. A synctest test that waited while a listener was held no longer hangs until the package timeout.
BF-c9f1cff
The experimental client takes Pi's ByteTransportFactory shape: ClientOptions.TransportFactory returns a…
- Source: PiG's own defect
- Symptom: The experimental client takes Pi's
ByteTransportFactoryshape:ClientOptions.TransportFactoryreturns aByteTransportwhoseSend(ctx, chunk)returns when the write settles. - Root cause: Production keeps Pi's semantics through the Pi shape: a handler call made during the factory is live (the handler gate is removed; the unix transport starts reading when the Connection has taken it, readStarter), a transport that admits sends itself (Submit) is handed each frame before Connection.Send returns (finding 22; pipelining, finding 23), and a failed Connect joins the transport's lifetime. The package experimental seam and binding-guard tests use the Pi shape.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
4789fa51f0(2026-10-08),5dbfed1576(2026-10-08),8655ff6ae5(2026-10-08),c9f1cffbd5(2026-10-08) - Tests:
ai/faux_duration_test.go::TestFauxScriptedDurationMsReachesEveryMessage,ai/faux_scripted_fields_oracle_test.go::TestFauxScriptedMessageMembersMatchPi,coding/extension/host/subprocess/runtime_node_user_bash_operations_test.go::TestNodeRuntimeUserBashOperations,coding/extension/host/subprocess/user_bash_operations_test.go::TestUserBashOperationsExecAbortsWithTheContext,coding/extension/host/subprocess/user_bash_operations_test.go::TestUserBashOperationsReleaseTheExtensionHandleWhenTheHostDropsThem,coding/extension/host/subprocess/user_bash_operations_test.go::TestUserBashOperationsReplyWithResultOrWithoutHandleIsLeftRaw,extensions/sdk-py/tests/test_user_bash_operations.py,extensions/sdk/user_bash_operations_test.go::TestUserBashOperationsStayInTheExtensionUntilTheHostReleasesThemand 3 more in docs/bug-ledger.json - Release note: The experimental client takes Pi's
ByteTransportFactoryshape:ClientOptions.TransportFactoryreturns aByteTransportwhoseSend(ctx, chunk)returns when the write settles. The unix transport uses it, and a transport that admits sends itself (Submit) is handed each frame beforeConnection.Sendreturns.
BF-c9fbfcc
Inline images render in Herdr again: TERM_PROGRAM=herdr detects the Kitty graphics protocol, as in Pi 1.2.0
- Source: PiG's own defect
- Symptom: Inline images render in Herdr again:
TERM_PROGRAM=herdrdetects the Kitty graphics protocol, as in Pi 1.2.0. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
c9fbfccced(2026-10-12, #10774) - Tests:
tui/terminal_image_upstream_test.go::TestUpstreamTerminalImageCapabilities - Release note: Inline images render in Herdr again:
TERM_PROGRAM=herdrdetects the Kitty graphics protocol, as in Pi 1.2.0. Hyperlinks stay on, and truecolor still followsCOLORTERM/TERM.
BF-ca06a0b
Tool arguments that TypeBox's Convert wraps in an array (an object where the schema asks for an array, such…
- Source: PiG's own defect
- Symptom: Tool arguments that TypeBox's Convert wraps in an array (an object where the schema asks for an array, such as
edits) keep the member order of the arguments' text, in the returned arguments and in the order of the validation errors, as in Pi. - Root cause: TypeBox's Convert (FromArray) wraps a value that is not an array in one when the schema asks for an array: {"edits":{...}} becomes {"edits":[{...}]}. Pi's wrapped object keeps the member order of the arguments' text.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ca06a0b4fd(2026-10-11) - Tests:
ai/validate_wrapped_order_oracle_test.go::TestValidateToolArgumentsKeepsTheSourceOrderOfWrappedObjectsLikePi - Release note: Tool arguments that TypeBox's Convert wraps in an array (an object where the schema asks for an array, such as
edits) keep the member order of the arguments' text, in the returned arguments and in the order of the validation errors, as in Pi. They came out byte-sorted.
BF-ca27ba6
The Go extension SDK accepts a native Provider object with no auth methods, as Pi's ProviderAuth (both…
- Source: PiG's own defect
- Symptom: It used to panic at load with "Provider requires auth", while the Node, Python and Rust SDKs accepted it.
- Root cause: Pi's ProviderAuth (packages/ai/src/auth/types.ts:252-255) makes apiKey and oauth both optional. The Go SDK rejected such a Provider at load with 'Provider requires auth' while the Node, Python and Rust SDKs accepted it. Found by the new scenario rows; providers-auth/provider-without-auth-methods-registers was red for Go (rig now observes a failed load) and is green after the fix. Also: model registry hasConfiguredAuth/isUsingOAuth/getProvider rows for all SDKs.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ca27ba6e1d(2026-10-09) - Tests:
ai/anthropic_oauth_test.go::TestAnthropicHistoryToolNames - Release note: The Go extension SDK accepts a native Provider object with no auth methods, as Pi's
ProviderAuth(bothapiKeyandoauthoptional) allows. It used to panic at load with "Provider requires auth", while the Node, Python and Rust SDKs accepted it.
BF-ca675e0
A list item whose text is [ ] or [x] with nothing after the spaces on the line is plain text, not a…
- Source: PiG's own defect
- Symptom: A list item whose text is
[ ]or[x]with nothing after the spaces on the line is plain text, not a task item (marked's listIsTask needs a non-whitespace character after the spaces), so- [ ]renders as- [ ]like Pi. - Root cause: marked's listIsTask is /^[[ xX]] +\S/. splitListTaskMarker accepted
[ ]followed by the end of the text or a newline (- [ ],- [ ] \n c), so the item rendered with a checkbox marker and a continuation indent of six; Pi keeps[ ]as the item's text under a plain bullet. - User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ca675e0af0(2026-10-11) - Tests:
tui/markdown_task_item_oracle_test.go::TestMarkdownTaskItemFirstLineIsTextMatchesPi,tui/markdown_task_item_oracle_test.go::TestMarkdownTaskItemNeedsATextAfterTheSpacesMatchesPi - Release note: A list item whose text is
[ ]or[x]with nothing after the spaces on the line is plain text, not a task item (marked's listIsTask needs a non-whitespace character after the spaces), so- [ ]renders as- [ ]like Pi.
BF-cb00273
OpenRouter's signature-only reasoning.text delta for Claude is kept and replayed with its reasoning block…
- Source: A Pi defect that PiG fixed first (divergence D122)
- Symptom: OpenRouter's signature-only
reasoning.textdelta for Claude is kept and replayed with its reasoning block (D122, pi-mono #9534). - Root cause: Pi openai-completions.ts:144-148 isOpenAIReasoningDetail requires text, so OpenRouter's signature delta is dropped and the replayed reasoning_details lose the signature.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/9534
- Removed when Pi fixes it: Pi keeps signature-only
reasoning.textdeltas. - Fix commits:
cb002735d6(2026-10-09) - Tests:
ai/openai_reasoning_signature_only_test.go::TestOpenAICompletionsKeepsSignatureOnlyReasoningText - Release note: OpenRouter's signature-only
reasoning.textdelta for Claude is kept and replayed with its reasoning block (D122, pi-mono #9534).
BF-cb21ee0
Markdown resolves link reference definitions as Pi does: [text][label], [label][] and [label] link to a…
- Source: PiG's own defect
- Symptom: Markdown resolves link reference definitions as Pi does:
[text][label],[label][]and[label]link to a[label]: urldefined anywhere in the message, and the definition line is no longer printed. - Root cause: Markdown now ports marked 18.0.11's link reference definitions. The def block tokenizer runs after HTML blocks. Definitions go into a links map for the whole document, where the first definition of a tag wins. Reference, collapsed and shortcut links resolve against that map, and references to defined tags are masked for emphasis, including the link-in-text check. marked registers every definition before it lexes any inline text. So a document whose definition follows a "[" is rendered a second time with the full map. A definition renders no lines, as in Pi: blanks around it are kept, and text that renders nothing returns [""].
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
c5796c60b1(2026-10-05),b2a820387a(2026-10-06),cb21ee0405(2026-10-06),d90c2f41b7(2026-10-08) - Tests:
test/parity/scenarios/interactive-rendering/44-assistant-underscore-emphasis.toml,tui/highlight_strip_test.go::TestStripSyntaxHighlightBuildOmitsHljs,tui/markdown_autolink_test.go::TestAutoLinkTokensMatchMarked,tui/markdown_autolink_test.go::TestAutoLinkURLRuleAtTokenStarts,tui/markdown_blockquote_style_test.go::TestMarkdownBlockquoteDefaultStyleMatchesUpstreamBytes,tui/markdown_blockquote_style_test.go::TestMarkdownEmphasisAndSetextMatchUpstreamBytes,tui/markdown_blockquote_style_test.go::TestMarkdownParagraphSpacingBeforeInterruptingBlockMatchesUpstream,tui/markdown_marked_corpus_test.go::TestMarkdownMarkedCorpusMatchesPiand 6 more in docs/bug-ledger.json - Release note: Markdown resolves link reference definitions as Pi does:
[text][label],[label][]and[label]link to a[label]: urldefined anywhere in the message, and the definition line is no longer printed. A list item whose text starts more than four spaces after its marker, as after a tab, renders as an indented code block.
BF-cc9370b
A Node extension's setWidget, setHeader and setFooter factories receive the same tui as a…
- Source: PiG's own defect
- Symptom: A Node extension's
setWidget,setHeaderandsetFooterfactories receive the sametuias actx.ui.customfactory, as in Pi:showOverlay,hideOverlay,setFocusandterminalwork there, andtui.requestRender()also redraws the overlays the extension shows. - Root cause: TestCustomOverlayFocusChangesReachSDKs (bridge in place, SDK changes and pack reverted): every cell (subprocess-go, -node, -node-packed, -rust, -python, fused-go) "the component did not render focused=false after it lost focus".
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1848633092(2026-10-13),2a055a7a3a(2026-10-13),a9058bf992(2026-10-13),ab0ee713e2(2026-10-13),cc9370b309(2026-10-13),d87b70c54c(2026-10-13),ec2080a75f(2026-10-13) - Tests:
coding/extension/host/subprocess/host_calls_test.go::TestReadLoopWidgetPushOrdersAgainstQueuedSlotCalls,coding/extension/host/subprocess/node_library_bundle_test.go::TestNodeLibraryBundlesKeepSharedIdentitiesWithoutRawCatalogLoads,coding/extension/host/subprocess/runtime_node_overlay_handle_test.go::TestNodeFactoryTuiShowOverlayHandleIsSynchronous,coding/extension/host/subprocess/runtime_node_overlay_handle_test.go::TestNodeSurfaceFactoryTuiShowsOverlays,coding/extension/host/subprocess/runtime_node_prompt_end_test.go::TestNodePromptEndWaitEndsWhenAnotherProcessTurnsTheLoop,coding/extension/host/subprocess/ui_bridge_raw_overlay_test.go::TestRawOverlayEndsWithItsConnection,coding/extension/host/subprocess/ui_bridge_raw_overlay_test.go::TestRawOverlayShowsAtOnceAndEndsWithItsHide,coding/extension/host/subprocess/ui_bridge_raw_overlay_test.go::TestRawOverlayWithoutTUIIsNotShownand 22 more in docs/bug-ledger.json - Release note: A Node extension's
setWidget,setHeaderandsetFooterfactories receive the sametuias actx.ui.customfactory, as in Pi:showOverlay,hideOverlay,setFocusandterminalwork there, andtui.requestRender()also redraws the overlays the extension shows.
BF-ccaade2
The /thinking selector edits its search like Pi's input, selects with the input's submit key, and no longer…
- Source: PiG's own defect
- Symptom: The
/thinkingselector edits its search like Pi's input, selects with the input's submit key, and no longer repeats a selection or cancel on the next navigation key. - Root cause: (1) tui.input.submit (ctrl+s here) must select through the search Input's onSubmit -> selectList.handleInput("\r"); Pig's host-owned TextInput only marked itself done. (2) Pig re-fired onSelect/onCancel on every later navigation key because it read the list's sticky Done state; Pi fires once per confirm/cancel key (new FilterableList.Reset, called by the selector after it dispatches). Scenario settings/06 (/thinking, m Down Left d -> '> dm') is red on the old selector ('> md') and byte-equal now.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d1f258f335(2026-10-06),0ae3fcf17c(2026-10-07),ccaade27af(2026-10-07) - Tests:
internal/codingagent/extension_dialog_remapping_test.go::TestExtensionDialogRemappingThroughOwnerLoop,internal/codingagent/thinking_selector_oracle_test.go::TestThinkingSelectorInputMatchesPi,test/parity/scenarios/settings/06-thinking-selector-search-editing.toml,test/parity/scenarios/settings/08-settings-search-editing.toml,tui/cancellable_loader_input_test.go::TestCancellableLoaderCancelFollowsTheBindingAndCallsOnAbortEachPress,tui/cancellable_loader_input_test.go::TestCancellableLoaderCancelWithoutOnAbortOnlyAborts,tui/extension_dialog_bindings_test.go::TestExtensionDialogsMatchPiBindingsAndPalette,tui/oauth_selector_input_test.go::TestOAuthSelectorInputMatchesPiand 7 more in docs/bug-ledger.json - Release note: The
/thinkingselector edits its search like Pi's input, selects with the input's submit key, and no longer repeats a selection or cancel on the next navigation key.
BF-cd7fa1c
A read-only config root no longer prints warnings at every start
- Source: PiG's own defect
- Symptom: A read-only config root no longer prints warnings at every start.
- Root cause: A sync keeps the four most recently used bundles.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
395c46cbf2(2026-10-09),69b9811164(2026-10-09),852652e3ae(2026-10-09),da99854d1b(2026-10-09),cd7fa1ca25(2026-10-10) - Tests:
coding/cli/piglet_verify_cache_startup_test.go::TestSignedPigletBinaryRunsWhenItsVerifyCacheCannotBeWritten,coding/cli/readonly_config_root_binary_test.go::TestReadOnlyConfigRootStartsQuietlyAndNamesOnlyDocsThatExist,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,coding/extension/pigsdk/pigsdk_version_share_test.go::TestWarmStartRecordsTheUseOfThisBuildsSDKDirectories,internal/codingagent/auth_guidance_test.go::TestNoTextNamesADocsBundleStartupCouldNotWrite,internal/configroot/configroot_test.go::TestDocsDirIsThePigdocsBundleUnderTheConfigRoot,internal/contentdirs/contentdirs_test.go::TestEvictKeepsTheMostRecentlyUsedAndTheCallersOwn,internal/contentdirs/contentdirs_test.go::TestNameRequiresAHexHashand 7 more in docs/bug-ledger.json - Release note: A read-only config root no longer prints warnings at every start. The extension SDK staging warning stays silent there, because each build reads only its own staged SDK directories and nothing another build staged can stand in for them, and a signed Piglet Binary no longer warns that it cannot write its verification cache; its next start checks it in full again. Other write errors still warn. The agent directory must stay writable, as in Pi.
BF-cda6e3a
PiG opens a session file that holds a valid JSON line that is not an entry (an array, true, a string) or an…
- Source: PiG's own defect
- Symptom: PiG opens a session file that holds a valid JSON line that is not an entry (an array,
true, a string) or an entry whoseidorparentIdis not a string, as Pi does, instead of failing withjson: cannot unmarshal … SessionEntryBase. - Root cause:
- sessionentry keeps an id or parentId that is not a string as the key of its JavaScript value. The key starts with a 0xFF byte, which no decoded string holds. An absent id or parentId is undefined, and a null id is null. Each parsed object or array gets a key of its own, because Map compares objects by identity. - Truthy is JavaScript truthiness. - ValueJSON writes the value back. A child's parentId is that value, and is omitted when undefined. - WireID is the leaf as a host-call string. - A LazyEntry keeps the keys it was indexed with. - The load no longer re-decodes the base of an undescribed record. Its leaf is the record's id, or null when that id is null. - getBranch, the context…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),cda6e3a715(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_file_entry_union_test.go::TestSessionKeepsRecordsThatAreNotEntriesAsPiDoes,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,internal/sessionentry/lazy_test.go::TestLazyEntryKeepsTheKeysOfObjectValuedIDsand 3 more in docs/bug-ledger.json - Release note: PiG opens a session file that holds a valid JSON line that is not an entry (an array,
true, a string) or an entry whoseidorparentIdis not a string, as Pi does, instead of failing withjson: cannot unmarshal … SessionEntryBase. The leaf, the branch, the context and theparentIdof the next entry follow Pi: after a line with noidthe next entry has noparentIdmember, after an entry whoseidis5it has"parentId":5, and a falsyparentIdends a branch.
BF-cdf0be7
Markdown rendering matches Pi on blank lines at the start and end of the text, empty headings and closing #…
- Source: PiG's own defect
- Symptom: Markdown rendering matches Pi on blank lines at the start and end of the text, empty headings and closing
#runs, and unterminated code fences: a text that starts with a blank line begins with one empty row, a trailing line of spaces after a list, table or quote adds one, a closing run of#is dropped from a heading, an empty heading keeps its row, and an unterminated fence does not show the empty line its final newline splits off. - Root cause: TestParseMatchesTheLibraryOnMutatedCorpus and TestLexerMatchesTheLibraryOnMutatedCorpus compare eemeli/yaml 2.9.0 with Parse/lex on 6000 mutated corpus texts (40 differed before, 15-17 per 20000 after the first fixes). Fixed: an unterminated quote (buffer.substring(0, -1) is empty: the error is at the end of input, not after the first line); a line starting with a non-ASCII character reduced the continuation indent because the lexer looked at the second byte, not the second UTF-16 unit (charAt(1)); Invalid escape sequence raw text and the 1024-unit implicit key limit counted bytes; anchors reject JavaScript whitespace (RE2 \s is narrower); -0 is 0 as an omap/set key; Buffer.from(.., base64)…
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
b5b7b43f82(2026-10-10),cdf0be76e4(2026-10-10) - Tests:
coding/rpcclient/jsonl_reader_oracle_test.go::TestJSONLReaderMatchesPi,internal/codingagent/session_load_oracle_test.go::TestLoadEntriesFromFileMatchesPiOnCorruptedFiles,internal/codingagent/tools/edit_tool_oracle_test.go::TestEditToolMatchesPi,internal/yaml12/parse_mutation_test.go::TestLexerMatchesTheLibraryOnMutatedCorpus,internal/yaml12/parse_mutation_test.go::TestParseMatchesTheLibraryOnMutatedCorpus,internal/yaml12/parse_mutation_test.go::TestParseMatchesTheLibraryOnUTF16Offsets,internal/yaml12/parse_test.go::TestParseMatchesTheLibrary,tui/markdown_render_oracle_test.go::TestMarkdownRenderMatchesPi - Release note: Markdown rendering matches Pi on blank lines at the start and end of the text, empty headings and closing
#runs, and unterminated code fences: a text that starts with a blank line begins with one empty row, a trailing line of spaces after a list, table or quote adds one, a closing run of#is dropped from a heading, an empty heading keeps its row, and an unterminated fence does not show the empty line its final newline splits off.
BF-cdf2887
fix(ai): keep a reused-id tool result in its own turn during the D48 repair
- Source: PiG's own defect
- Symptom: fix(ai): keep a reused-id tool result in its own turn during the D48 repair
- Root cause: RepairToolFlow moved the result of the newest turn back to an earlier unanswered call that shares its id (providers that number calls per response), so the model saw its current call answered 'No result provided' and its real result attached to an older turn. Pi's request for that history is intact. A result now does not move back past a turn that reopens its id, and the drop pass counts open calls per turn.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
cdf2887af3(2026-10-10) - Tests:
ai/tool_flow_repair_property_test.go::TestRepairToolFlowChangesOnlyBrokenRequests,ai/tool_flow_repair_test.go::TestRepairToolFlow
BF-ce0f352
createPowerShellTool now uses the operations option
- Source: A fix Pi shipped that PiG ported
- Symptom:
createPowerShellToolnow uses theoperationsoption. - Root cause: Pi: powershell.ts:53-56 passes options.operations (CreatePowerShellTool dropped it); session-manager.ts:1025-1054 setSessionFile; theme.ts:1135 getSelectListTheme; dynamic-border.ts:14 color callback; slash-commands.ts:4 SlashCommandSource "skill".
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
63b4fc80e7(2026-10-05),ebc1768e5f(2026-10-06),ce0f352637(2026-10-07) - Tests:
durable/env/node/conformance_cases_test.go::TestCreateEnvConformanceDetectsABrokenEnvironment,chord/output/output_test.go::TestOutputBuffer,chord/output/output_test.go::TestToolOutputBounds,durable/harness/harness_registry_test.go::TestSettingsKeepTheDefaultOfAProgressIntervalGivenAsUndefined,durable/tools/powershell_upstream_test.go::TestPowerShellFallsBackToWindowsPowerShellAndReportsTheLastStartFailure,durable/tools/powershell_upstream_test.go::TestPowerShellRunsRealPowerShellWithUTF8Output,durable/tools/powershell_upstream_test.go::TestPowerShellRunsTheCommandWithPwshAsOneArgumentForcingUTF8Output,durable/tools/powershell_upstream_test.go::TestReadReadsALogThatGrowsWhileItIsReadand 5 more in docs/bug-ledger.json - Release note:
createPowerShellToolnow uses theoperationsoption. The PowerShell tool dropped it and always ran commands through the local PowerShell.
BF-ce0f352-2
The editor's autocomplete list takes its colors from one getSelectListTheme equivalent, the same function…
- Source: PiG's own defect
- Symptom: The editor's autocomplete list takes its colors from one
getSelectListThemeequivalent, the same function the other select lists use. - Root cause: Pi: powershell.ts:53-56 passes options.operations (CreatePowerShellTool dropped it); session-manager.ts:1025-1054 setSessionFile; theme.ts:1135 getSelectListTheme; dynamic-border.ts:14 color callback; slash-commands.ts:4 SlashCommandSource "skill".
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
38b0f0c036(2026-10-05),ce0f352637(2026-10-07) - Tests:
durable/env/node/conformance_cases_test.go::TestCreateEnvConformanceDetectsABrokenEnvironment,internal/codingagent/session_set_file_test.go::TestSetSessionFileOnAnInMemoryManagerDoesNotPersist,internal/codingagent/session_set_file_test.go::TestSetSessionFileSwitchesThePersistedManager,internal/codingagent/slash_command_source_test.go::TestSlashCommandCatalogListsPromptAndSkillSources,internal/codingagent/tools/bash_tool_options_test.go::TestCreatePowerShellToolRunsCustomOperationsAfterTheSpawnHook,tui/highlight_test.go::TestCliHighlightThemeColorsSubstitutionsWithTheTextColor,tui/highlight_test.go::TestHighlightCode_ColorsEachLineOfPythonDocstringsIndependently,tui/select_list_theme_test.go::TestGetSelectListThemeColorsFromTheActiveThemeand 2 more in docs/bug-ledger.json - Release note: The editor's autocomplete list takes its colors from one
getSelectListThemeequivalent, the same function the other select lists use.
BF-ce366dd
fix(coding): resume restores the transcript's tools only for the unchanged default selection…
- Source: PiG's own defect
- Symptom: fix(coding): resume restores the transcript's tools only for the unchanged default selection; --no-builtin-tools wins [SIGNOFF-PENDING]
- Root cause: The guard checked AllowedTools/NoTools, but the CLI's plain --no-builtin-tools sets SkipBuiltinTools with NoTools "", so a resume under -nbt reactivated the transcript's extension tools (opt_tool) where the explicit empty selection should win. The guard is now usesDefaultTools(opts) (sdk.ts:472) without +name/-name entries. The BuildContext projection before restore is dropped: restoreToolsFromTranscript is a no-op without a system message, so a resume no longer projects the branch twice.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
ce366ddf14(2026-10-10) - Tests:
coding/cli/rpc_resume_tools_test.go::TestRPCResumeKeepsDynamicallyActivatedTools,coding/session_resume_tools_test.go::TestResumeRestoresTheTranscriptsActiveTools
BF-ce42877
durable/images.CreatePhotonImages, and durable/images/node.CreateNodePhotonImages with Worker: false…
- Source: PiG's own defect
- Symptom: Concurrent calls prepared side by side.
- Root cause: Pi's createPhotonImages runs prepare's synchronous work on the calling thread, so calls are prepared one after another in call order. The turn-taking queue moves from node's Worker false path into durable/images (sequential.go) around the native preparer, and node's Worker false returns CreatePhotonImages again, so the order lives in one place. A caller cancelled while it waits returns its context error and the calls after it keep their order. The node tests pin the worker's own order; ledger rows and the changelog fragment name both.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),8435c98530(2026-10-13),ce42877426(2026-10-13) - Tests:
durable/images/node/worker_test.go::TestTheWorkerPreparesImagesInCallOrder,durable/images/node/worker_test.go::TestTheWorkerPreparesOneImageAtATime,durable/images/node/worker_test.go::TestWorkerFalsePreparesWithThePlainProcessor,durable/images/sequential_test.go::TestCreatePhotonImagesPreparesImagesInCallOrder,durable/images/sequential_test.go::TestCreatePhotonImagesPreparesOneImageAtATime,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellationand 4 more in docs/bug-ledger.json - Release note:
durable/images.CreatePhotonImages, anddurable/images/node.CreateNodePhotonImageswithWorker: false, which returns it, prepare images one at a time in call order on the caller's goroutine, as Pi'screatePhotonImagesdoes on its calling thread. Before, concurrent calls prepared side by side. A caller whose context is cancelled while it waits returns the context's error and does not hold up or reorder the calls after it.
BF-ce8587e
On macOS, reading an image or file paths from the clipboard and writing text to it no longer risk passing…
- Source: PiG's own defect
- Symptom: On macOS, reading an image or file paths from the clipboard and writing text to it no longer risk passing AppKit an address in freed memory.
- Root cause: ### Problem
- User impact: Behavior differed from Pi
- Platform: macOS
- Fixed in: 0.5.0
- Fix commits:
ce8587ef40(2026-10-07, #179) - Tests:
internal/nativeplatform/clipboard_darwin_filepaths_test.go::TestDarwinPasteboardFilePaths,internal/nativeplatform/clipboard_darwin_stack_test.go::TestDarwinPasteboardBuffersSurviveAStackMove,internal/nativeplatform/clipboard_darwin_stack_test.go::TestDarwinPasteboardTextRoundTrip - Release note: On macOS, reading an image or file paths from the clipboard and writing text to it no longer risk passing AppKit an address in freed memory. When Go moved the goroutine stack during the call, AppKit could read a stale array or text buffer. These buffers now stay in place for the call.
BF-ced3a9c
RPC model objects of built-in models carry "type":"chat" (get_available_models, get_state, set_model…
- Source: PiG's own defect
- Symptom: RPC model objects of built-in models carry
"type":"chat"(get_available_models,get_state,set_model,cycle_model), as Pi's do, and so do Radius models; amodels.jsonmodel has notype, even when its definition writes one. - Root cause: Row 20: set Type chat in the shared GeneratedModel.ToModel, which also converts models.json entries (nativeModelFromEntry), so a typeless custom model reported "type":"chat" where Pi's has none (provider-composer.ts createCustomModel spreads the definition), and the startup model (coding/model.go modelFromEntry) still dropped it, so get_state had no type. ModelEntry now carries the registry type: chat for a catalog entry, the definition's value for a models.json or native entry. The deleted TestRPCModelProjectionCarriesModelType is restored, and the unit test no longer pins "type" first: Pi's gpt-4o object has it last.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ced3a9c4d4(2026-10-10) - Tests:
coding/cli/rpc_model_type_binary_test.go::TestRPCModelObjectsCarryTheRegistryModelType,coding/cli/rpc_model_type_test.go::TestRPCBuiltinChatModelCarriesType,coding/cli/rpc_model_type_test.go::TestRPCModelProjectionCarriesModelType,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,mcp/oauth/parse_url_node_test.go::TestParseURLMatchesNodeNewURL - Release note: RPC model objects of built-in models carry
"type":"chat"(get_available_models,get_state,set_model,cycle_model), as Pi's do, and so do Radius models; amodels.jsonmodel has notype, even when its definition writes one.
BF-ced3a9c-2
A URL of a non-special scheme keeps an explicit port 0 (ssh://git@host:0/repo, an MCP OAuth redirect such…
- Source: PiG's own defect
- Symptom: A URL of a non-special scheme keeps an explicit port 0 (
ssh://git@host:0/repo, an MCP OAuth redirect such asmyapp://host:0/cb), as Node'snew URLdoes. - Root cause: Row 20: set Type chat in the shared GeneratedModel.ToModel, which also converts models.json entries (nativeModelFromEntry), so a typeless custom model reported "type":"chat" where Pi's has none (provider-composer.ts createCustomModel spreads the definition), and the startup model (coding/model.go modelFromEntry) still dropped it, so get_state had no type. ModelEntry now carries the registry type: chat for a catalog entry, the definition's value for a models.json or native entry. The deleted TestRPCModelProjectionCarriesModelType is restored, and the unit test no longer pins "type" first: Pi's gpt-4o object has it last.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
ced3a9c4d4(2026-10-10) - Tests:
coding/cli/rpc_model_type_binary_test.go::TestRPCModelObjectsCarryTheRegistryModelType,coding/cli/rpc_model_type_test.go::TestRPCBuiltinChatModelCarriesType,coding/cli/rpc_model_type_test.go::TestRPCModelProjectionCarriesModelType,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,mcp/oauth/parse_url_node_test.go::TestParseURLMatchesNodeNewURL - Release note: A URL of a non-special scheme keeps an explicit port 0 (
ssh://git@host:0/repo, an MCP OAuth redirect such asmyapp://host:0/cb), as Node'snew URLdoes.
BF-cf4fff8
A project whose only configuration is a project mcp.json (.pig/mcp.json, or .pi/mcp.json with…
- Source: PiG's own defect
- Symptom: A project whose only configuration is a project
mcp.json(.pig/mcp.json, or.pi/mcp.jsonwithPIG_USE_PI_DIRS=1, D2) now requires project trust, as Pi'sTRUST_REQUIRING_PROJECT_CONFIG_RESOURCESdoes. - Root cause: A real-binary test runs print, print --no-session, JSON and RPC modes in a project whose only configuration is a project mcp.json with a stdio server. The server process must not start and its tool must not reach the model; a saved trust decision starts it. Without "mcp.json" in the trust list every untrusted mode starts the server.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
cf4fff8f78(2026-10-10) - Tests:
coding/cli/mcp_builtin_binary_test.go::TestBuiltinMCPExtensionStartsProjectServersOnlyInATrustedProject,test/parity/scenarios/project-trust/22-mcp-json-only-project-requires-trust.toml - Release note: A project whose only configuration is a project
mcp.json(.pig/mcp.json, or.pi/mcp.jsonwithPIG_USE_PI_DIRS=1, D2) now requires project trust, as Pi'sTRUST_REQUIRING_PROJECT_CONFIG_RESOURCESdoes. PiG leftmcp.jsonout of that list, so such a project counted as having nothing to trust and the commands of its stdio MCP servers started without a prompt in every mode. Interactive mode now asks for trust. Print, JSON and RPC modes, without a saved decision ordefaultProjectTrust: "always", now ignore the file, as they do for other project configuration.
BF-cf648c7
A restarted extension, isolated or in a recovered packed cell, now offers the commands, flags, shortcuts and…
- Source: PiG's own defect
- Symptom: A restarted extension, isolated or in a recovered packed cell, now offers the commands, flags, shortcuts and tools its new process registered; before, it kept the first registration's.
- Root cause: The register message carried a command argument hint (
args, sent only by the Node runtime) and a per-handlercan_blockflag (sent by Node, Python and Rust). The host read neither, and Pi has neither concept: RegisteredCommand has no argument hint (types.ts:1541-1547, loader.ts:303) and every handler may return a result (runner.ts). The Go and Node SDK APIs never exposed can_block; Ruston_event/on_event_resultand Pythonon_eventdid, so the SDK interfaces differed for no behaviour. - User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
cf648c7b8b(2026-10-09) - Tests:
coding/extension/host/subprocess/host_test.go::TestHost_BuildExtension_Tools,coding/extension/host/subprocess/host_test.go::TestProtocol_RegisterRoundTrip,coding/extension/host/subprocess/registration_fields_wiring_test.go::TestRegistrationDeclarationsKeepEveryField - Release note: A restarted extension, isolated or in a recovered packed cell, now offers the commands, flags, shortcuts and tools its new process registered; before, it kept the first registration's. Shortcuts keep their registration order.
BF-cf7f368
A local file listed as a Package with autoload: false still loads as one enabled extension, and when a…
- Source: PiG's own defect
- Symptom: A local file listed as a Package with
autoload: falsestill loads as one enabled extension, and when a project lists a Package withautoload: false, every user-settings entry for that Package is kept, as in Pi'sresolveLocalExtensionSourceanddedupePackages. - Root cause: TestLoadMcpConfigMatchesPiOnRandomFiles loads 1500 random global and project mcp.json files through Pi's loadMcpConfig and PiG's LoadMcpConfig: 187 differed, among them every file with a numeric server name, because Object.entries lists array-index keys first and orderedjson.Object kept file order. Object.Set now inserts an array-index key at its ascending place before the other keys (Keys, MarshalJSON, Clone follow), with TestObjectEnumeratesArrayIndexKeysFirst. Dependent packages (codingagent, extension, mcpext, mcp, packagecontent) pass; the failures left in tools and the node-backed subprocess tests are the fd/rg and node-shim environment ones.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1429665458(2026-10-09),24232ef878(2026-10-09),2b3403045b(2026-10-09),5ceebfdae9(2026-10-09),667604151e(2026-10-09),3def2a7bd8(2026-10-10),7e1a05ccfd(2026-10-10),a46f930200(2026-10-10),cf7f36805a(2026-10-10),d8bed22101(2026-10-10) - Tests:
ai/anthropic_messages_response_oracle_test.go::TestAnthropicMessagesResponseMatchesPi,ai/auth_load_oracle_test.go::TestAuthStorageOnCorruptedFileMatchesPi,ai/auth_opaque_entries_test.go::TestAuthStorageKeepsEntriesItCannotTypeAcrossWrites,ai/google_generative_ai_payload_oracle_test.go::TestGoogleGenerativeAIPayloadMatchesPi,ai/google_tool_call_id_test.go::TestGoogleConvertMessages_EmitsToolCallIdWhenRequired,ai/google_tool_call_id_test.go::TestGoogleConvertMessages_TruncatesToolCallIdTo64,ai/openai_completions_payload_oracle_test.go::TestOpenAICompletionsPayloadMatchesPi,ai/openai_completions_response_oracle_test.go::TestOpenAICompletionsResponseMatchesPiand 15 more in docs/bug-ledger.json - Release note: A local file listed as a Package with
autoload: falsestill loads as one enabled extension, and when a project lists a Package withautoload: false, every user-settings entry for that Package is kept, as in Pi'sresolveLocalExtensionSourceanddedupePackages.
BF-d0c2727
fix(codingagent): decode a Node request's client identity from its fields; regenerate the normalization…
- Source: PiG's own defect
- Symptom: fix(codingagent): decode a Node request's client identity from its fields; regenerate the normalization inventory
- Root cause: It reads userAgent, version and attribution from the decoded fields with the same errors. The test's Pi user-agent pattern no longer uses (?i).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
d0c2727053(2026-10-21) - Tests: None recorded.
BF-d11ca55
pig piglet build --format binary from a Pig checkout with a go.work builds a Piglet whose fused Go…
- Source: PiG's own defect
- Symptom: The build stopped with
go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay, because workspace mode needs checksums thatgo mod tidydoes not write to the extension'sgo.sum. - Root cause: Before, the build stopped with
go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay, because workspace mode needs checksums thatgo mod tidydoes not write to the extension'sgo.sum. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
da99854d1b(2026-10-09),8a5162f352(2026-10-10),d11ca55cdb(2026-10-10) - Tests:
coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,coding/pigletbuild/testmain_test.go::TestMain,coding/pigletbuild/workspace_sums_test.go::TestAppendMissingLinesKeepsBaseAndAddsOnlyNewLines,coding/pigletbuild/workspace_sums_test.go::TestBuildNativeArtifactCompletesWorkspaceChecksumsOffline,coding/pigletbuild/workspace_sums_test.go::TestCompleteWorkspaceSumsSkipsAModuleCacheSource,coding/pigletbuild/workspace_sums_test.go::TestScratchWorkFileAnchorsRelativePaths - Release note:
pig piglet build --format binaryfrom a Pig checkout with ago.workbuilds a Piglet whose fused Go extension imports an external module, such as ana2a-gov2 package. Before, the build stopped withgo: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay, because workspace mode needs checksums thatgo mod tidydoes not write to the extension'sgo.sum. The build now adds them to its own overlay and still never writes the checkout.
BF-d133e3d
Codemode scripts that patch built-ins, for example Array.prototype.toJSON = ..., no longer break the tool…
- Source: A fix Pi shipped that PiG ported
- Symptom: Codemode scripts that patch built-ins, for example
Array.prototype.toJSON = ..., no longer break the tool call: built-ins are frozen before the script runs, and a malformed message from the sandbox fails the run with asandboxerror. - Root cause: Regenerate shims/pi-dist from the pinned @earendil-works packages (automation/gen/vendor-pi-dist.sh), runtime.mjs getPromptGuidelines and hiddenTools, runtime-node.zip and its digest (go generate).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
03d9bbfce9(2026-10-05),d133e3d912(2026-10-05) - Tests:
codemode/bridge_test.go::TestAcceptsWellFormedBridgePayloads,codemode/bridge_test.go::TestReportsABrokenBridgeAsASandboxError,codemode/bridge_test.go::TestReportsADuplicateCallIDAsASandboxError,codemode/sandbox_test.go::TestEmbeddedSourcesMatchTheirRecordedHashes,codemode/sandbox_test.go::TestFreezesIntrinsicsThatAreOnlyReachableFromInstances,codemode/sandbox_test.go::TestIgnoresPatchesToBuiltinsAndBuiltinGlobals,codemode/sandbox_test.go::TestReportsErrorsWhoseNameOrMessageIsNotAString,codemode/sandbox_test.go::TestStillLetsInstancesOverridePropertiesOfFrozenPrototypes - Release note: Codemode scripts that patch built-ins, for example
Array.prototype.toJSON = ..., no longer break the tool call: built-ins are frozen before the script runs, and a malformed message from the sandbox fails the run with asandboxerror.
BF-d133e3d-2
MCP OAuth sign-in no longer fails with invalid_redirect_uri on OpenID Connect servers: client registration…
- Source: A fix Pi shipped that PiG ported
- Symptom: MCP OAuth sign-in no longer fails with
invalid_redirect_urion OpenID Connect servers: client registration sendsapplication_type,nativefor loopback and custom-scheme redirects. - Root cause: Regenerate shims/pi-dist from the pinned @earendil-works packages (automation/gen/vendor-pi-dist.sh), runtime.mjs getPromptGuidelines and hiddenTools, runtime-node.zip and its digest (go generate).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
affc944ad8(2026-10-05),d133e3d912(2026-10-05) - Tests:
ai/assistant_retry_test.go::TestIsRetryableAssistantErrorClassification,mcp/oauth/oauth_test.go::TestMCPOAuthRegistersWithAnApplicationTypeDerivedFromTheRedirectURIsUnlessOneIsSet - Release note: MCP OAuth sign-in no longer fails with
invalid_redirect_urion OpenID Connect servers: client registration sendsapplication_type,nativefor loopback and custom-scheme redirects.
BF-d1cbde3
The pi-messages, Cloudflare Workers AI and TypeSafe System One, and OpenAI decisions request URLs are built…
- Source: PiG's own defect
- Symptom: The pi-messages, Cloudflare Workers AI and TypeSafe System One, and OpenAI decisions request URLs are built with the WHATWG URL parser, as Pi's
new URLdoes: hosts are normalized, a base URL with a query or fragment resolves against its path, anddebug=1is added to an existing query in order and in form encoding instead of with sorted keys. - Root cause: The previous net/url code differed on 3529 of 4500 (red before, measured with a scratch copy of the old functions): JoinPath on a base with a query or fragment, hosts and userinfo left unnormalized, url.Values.Encode sorting and re-escaping the existing query where URLSearchParams keeps order and form encoding, and invalid URLs accepted. pi_messages.go and system_one.go now parse with nodeurl.ParseURL; new nodeurl URL.ResolveRelativePath (path-relative reference, empty path as '/', Node's drive-letter-prefix rule for file: bases), URL.WithSearchParam (URLSearchParams.set on the ordered pair list) and FormURLEncode, which replaces ai's private copy. The pi-messages error body now reports…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
31f4517fbe(2026-10-09),90882933f6(2026-10-09),b5356c4269(2026-10-09),d1cbde3321(2026-10-09),d80c1cd150(2026-10-09) - Tests:
ai/azure_config_oracle_test.go::TestAzureBaseURLNormalizationMatchesPi,ai/estimate_oracle_test.go::TestTokenEstimatesMatchPiOnSeededTranscripts,ai/radius_urls_oracle_test.go::TestRadiusURLsMatchPiOnSeededProbes,ai/service_urls_oracle_test.go::TestProviderServiceURLsMatchPiOnSeededProbes,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,internal/nodeurl/url_oracle_test.go::TestParseMatchesNodeURL - Release note: The pi-messages, Cloudflare Workers AI and TypeSafe System One, and OpenAI decisions request URLs are built with the WHATWG URL parser, as Pi's
new URLdoes: hosts are normalized, a base URL with a query or fragment resolves against its path, anddebug=1is added to an existing query in order and in form encoding instead of with sorted keys.
BF-d1f258f
The /settings search edits like Pi's search input: the cursor moves with the arrow keys, Ctrl+W, Ctrl+K…
- Source: PiG's own defect
- Symptom: The
/settingssearch edits like Pi's search input: the cursor moves with the arrow keys, Ctrl+W, Ctrl+K, Ctrl+Y, paste and the other input keys work, and any key the list does not own returns the selection to the first match. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d1f258f335(2026-10-06) - Tests:
test/parity/scenarios/settings/08-settings-search-editing.toml,tui/settings_list_input_test.go::TestSettingsListAnyDelegatedKeyResetsTheSelectionToTheFirstMatch,tui/settings_list_input_test.go::TestSettingsListBoundActionsRunInUpstreamOrder,tui/settings_list_input_test.go::TestSettingsListEmptyAndSingletonNavigationBoundaries,tui/settings_list_input_test.go::TestSettingsListSearchDelegatesEditingKeysToTheInput,tui/settings_list_input_test.go::TestSettingsListSubmenuReceivesEveryKeyIncludingCancel,tui/settings_list_input_test.go::TestSettingsListWithoutSearchIgnoresOtherKeysAndKeepsSelection,tui/settings_list_keys_test.go::TestSettingsListPageAndHomeEndKeysFollowSearchState - Release note: The
/settingssearch edits like Pi's search input: the cursor moves with the arrow keys, Ctrl+W, Ctrl+K, Ctrl+Y, paste and the other input keys work, and any key the list does not own returns the selection to the first match. Confirm and cancel keys that a user binds to the same key as up or down now run up or down first, as Pi does.
BF-d212cdf
pig --piglet piglets/standard/pig-standard.yaml no longer warns on every start that its piglogin…
- Source: PiG's own defect
- Symptom:
pig --piglet piglets/standard/pig-standard.yamlno longer warns on every start that itspigloginextension registers/sprite, so the built-inpig-loginextension was not loaded. - Root cause:
pig --piglet piglets/standard/pig-standard.yamlno longer warns on every start that itspigloginextension registers/sprite, so the built-inpig-loginextension was not loaded. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
d212cdf056(2026-10-13) - Tests:
coding/cli/standard_piglet_startup_test.go::TestStandardPigletStartsWithoutReplacedBuiltinWarning - Release note:
pig --piglet piglets/standard/pig-standard.yamlno longer warns on every start that itspigloginextension registers/sprite, so the built-inpig-loginextension was not loaded. PiG Standard now strips the built-inpig-login(strip.extensions: [pig-login]), and its ownpigloginextension owns/sprite.
BF-d249c70
SDK: Session.Dispose now releases the Session as Pi's dispose does
- Source: PiG's own defect
- Symptom: SDK:
Session.Disposenow releases the Session as Pi'sdisposedoes. - Root cause: Pi's AgentSession.dispose (agent-session.ts:1389-1416) is for "when completely done with the session". It drops every listener and disconnects from the agent, so nothing of the Session stays live. PiG's Dispose cleared the listeners but left two things running until Close: - the event forwarder goroutine: the Events stream stayed open, and with no reader, producers blocked once the buffers filled; - the model registry observer. The services outlive a Session across replacement, so the registry observer kept a disposed Session reachable. An embedder that ends a Session with Dispose alone, as with Pi, leaked both.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d249c707fe(2026-10-10) - Tests:
coding/session_dispose_leak_test.go::TestSessionDisposeReleasesEventProducers,coding/session_dispose_leak_test.go::TestSessionDisposeReleasesTheSession,coding/session_rpc33_observation_test.go::TestSessionObservationCloseJoinsClaimedCallback,coding/session_rpc33_observation_test.go::TestSessionObservationReservationCanceledBeforeAdmission - Release note: SDK:
Session.Disposenow releases the Session as Pi'sdisposedoes. ItsEventsstream closes, publishing and flushing events stop waiting, and the model registry no longer keeps a disposed Session reachable.
BF-d26d494
fix(tui): Editor.SetText resets the scroll offset
- Source: PiG's own defect
- Symptom: fix(tui): Editor.SetText resets the scroll offset
- Root cause: Pi's setText ends in setTextInternal, which sets scrollOffset = 0; Go kept the old offset.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
d26d494f78(2026-10-10) - Tests:
tui/editor_oracle_test.go::TestEditorMatchesPiOnFreshSequences,tui/editor_oracle_test.go::TestEditorMatchesPiOracle
BF-d276c00
pig piglet schema lists the apis strip list
- Source: PiG's own defect
- Symptom:
pig piglet schemalists theapisstrip list. - Root cause: pig piglet show lists the slot, the schema gains slots and removeSpec.slots, and a Binary's embedded Piglet carries no slots because the member is compiled in. The Binary record keeps the member's tree digest as a content-addressed frontend input.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f5190b9c48(2026-10-04),591f095784(2026-10-05),613c997d21(2026-10-05),d1c9dfddbb(2026-10-05),171aa56d63(2026-10-06),c20aeb351d(2026-10-06),ca7160566a(2026-10-06),1f98a7c315(2026-10-07),c0addf92cc(2026-10-07),44f92ab2d8(2026-10-08),d276c00c53(2026-10-08) - Tests:
ai/bedrock_option_fields_upstream_test.go::TestBedrockBearerTokenOptionPrecedence,ai/bedrock_option_fields_upstream_test.go::TestBedrockThinkingDisplayOption,ai/bedrock_provider_options_wire_test.go::TestBedrockAppliesPreparedHeadersAndRequestEnvironment,ai/bedrock_provider_options_wire_test.go::TestBedrockReservedHeaderMiddlewarePreservesSDKValues,ai/bedrock_provider_options_wire_test.go::TestBedrockReservedHeadersCannotBeOverriddenOrDeleted,ai/bedrock_provider_response_test.go::TestBedrockResponseHeadersUpstream,ai/bedrock_stream_events_upstream_test.go::TestBedrockProviderStreamEventsUpstream,ai/bedrock_transport_error_test.go::TestBedrockMidStreamTransportFailureMatchesTerminatedand 85 more in docs/bug-ledger.json - Release note:
pig piglet schemalists theapisstrip list.
BF-d2b1fc1
GitHub Copilot login reads an enterprise domain the way Pi does: an IPv6 host keeps its brackets, an…
- Source: PiG's own defect
- Symptom: GitHub Copilot login reads an enterprise domain the way Pi does: an IPv6 host keeps its brackets, an internationalized name becomes its
xn--form, a numeric host is normalized (1.2.3is1.2.0.3) or refused when out of range, a backslash ends the host, and a port above 65535 refuses the domain. - Root cause:
- nodeurl.Parse is
new URL(raw)without a base for any scheme (protocol, hostname, encoded query): special schemes through the WHATWG special authority, file through the file parser (localhost is empty), other schemes through the non-special authority, host and port states (forbidden host code points, IPv6, port digits and 65535, credentials or port without a host) and the opaque-host percent-encoding. - parseAuthorizationInput (anthropic.ts:29, openai-codex.ts:69, openrouter.ts:30) and Copilot's normalizeDomain (github-copilot.ts:41) used Go's url.Parse for schemes other than http/https: ws/wss/ftp/file and custom schemes accepted ports above 65535, '<' in a host, 'u@' without a host…
- nodeurl.Parse is
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
15a5c0b670(2026-10-09),23aa78b173(2026-10-09),2c04c1445a(2026-10-09),7c4d874438(2026-10-09),d2b1fc10a1(2026-10-09) - Tests:
ai/anthropic_pasted_state_test.go::TestAnthropicLoginSendsAnEmptyPastedStateAsPiDoes,ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,ai/trim_boundaries_test.go::TestOAuthPastedInputUsesECMAScriptTrim,ai/trusted_url_oracle_test.go::TestTrustedVerificationURLsMatchPi,internal/nodeurl/url_oracle_test.go::TestParseMatchesNodeURL - Release note: GitHub Copilot login reads an enterprise domain the way Pi does: an IPv6 host keeps its brackets, an internationalized name becomes its
xn--form, a numeric host is normalized (1.2.3is1.2.0.3) or refused when out of range, a backslash ends the host, and a port above 65535 refuses the domain. A domain given as a URL of a scheme other thanhttp,https,ws,wssorftpkeeps its host asnew URLreports it: the case as written, non-ASCII characters percent-encoded, and an empty host forfile://localhost. The Copilot token'sproxy-ep=claim is found anywhere in the token and used as written, as Pi does, instead of only at the start of a;-separated part and trimmed.
BF-d2b1fc1-2
Pasting an OAuth authorization into the Anthropic, OpenAI Codex or OpenRouter login now reads it the way Pi…
- Source: PiG's own defect
- Symptom: Pasting an OAuth authorization into the Anthropic, OpenAI Codex or OpenRouter login now reads it the way Pi does.
- Root cause:
- nodeurl.Parse is
new URL(raw)without a base for any scheme (protocol, hostname, encoded query): special schemes through the WHATWG special authority, file through the file parser (localhost is empty), other schemes through the non-special authority, host and port states (forbidden host code points, IPv6, port digits and 65535, credentials or port without a host) and the opaque-host percent-encoding. - parseAuthorizationInput (anthropic.ts:29, openai-codex.ts:69, openrouter.ts:30) and Copilot's normalizeDomain (github-copilot.ts:41) used Go's url.Parse for schemes other than http/https: ws/wss/ftp/file and custom schemes accepted ports above 65535, '<' in a host, 'u@' without a host…
- nodeurl.Parse is
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
15a5c0b670(2026-10-09),2c04c1445a(2026-10-09),d2b1fc10a1(2026-10-09) - Tests:
ai/anthropic_pasted_state_test.go::TestAnthropicLoginSendsAnEmptyPastedStateAsPiDoes,ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,ai/trim_boundaries_test.go::TestOAuthPastedInputUsesECMAScriptTrim,ai/trusted_url_oracle_test.go::TestTrustedVerificationURLsMatchPi,internal/nodeurl/url_oracle_test.go::TestParseMatchesNodeURL - Release note: Pasting an OAuth authorization into the Anthropic, OpenAI Codex or OpenRouter login now reads it the way Pi does. A
code#statepaste keeps only the text up to the next#as the state. A bare query string with a leading?, a;, or a malformed%escape keeps the code instead of dropping it. A URL of any scheme thatnew URLrefuses, such as one whose port is above 65535, is read as a plain code, not as a callback URL. An empty pasted state (code#orstate=) reaches Anthropic's token request as an empty state, as in Pi, instead of being replaced by the login's own state.
BF-d2d4384
A streamed write card no longer slows down as its content grows
- Source: PiG's own defect
- Symptom: A streamed write card no longer slows down as its content grows.
- Root cause: Since lw-tui2 every frame of a streaming write card encoded the whole partial argument object (ArgumentMembers) and decoded it twice, once for the header and once for the body, and the body split and normalised the whole content again: O(n) per frame, O(n^2) per stream (pauses write:400 6.6 -> 15 s CPU, 60 -> 395 MB own memory).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
9d5750c403(2026-10-13),d2d4384c0e(2026-10-13) - Tests:
ai/request_string_providers_test.go::TestProvidersSendTheSameBodyForEveryLargeStringOfARequest,internal/codingagent/interactive_events_write_frame_cost_norace_test.go::TestStreamedWriteCardFrameCostIsFlat,internal/codingagent/tool_builtin_renderers_write_equivalence_test.go::TestWriteRenderCallMatchesTheEagerRendererAtEveryPrefix,tui/tool_execution_streamed_args_test.go::TestStreamedArgsDrawTheHeaderOfACardWithoutDefinition,tui/tool_execution_streamed_args_test.go::TestStreamedArgsReachAFieldsReaderDecoded - Release note: A streamed write card no longer slows down as its content grows. Since streamed tool cards started drawing their partial arguments, every frame encoded the whole argument object and decoded it twice, and the write renderer split the whole content again, so a 400 KB write cost O(n²) (pauses write:400: 15 s CPU and 395 MB own memory, against 6.6 s and 60 MB before). The card now hands the built-in write renderer the object the streaming parser already holds, and the renderer extends its line count and highlighted lines from the previous frame, highlighting only the lines a frame draws. A frame allocates 77 KB at 400 KB received, as at 40 KB (3.1 MB before, 6.4 times the 40 KB frame). A settled write card keeps no highlighted copy of its content.
BF-d305b76
A Go extension whose go.mod replace (or a Rust extension whose pig-sdk path dependency) points at an SDK…
- Source: PiG's own defect
- Symptom: A Go extension whose
go.modreplace (or a Rust extension whosepig-sdkpath dependency) points at an SDK an earlier pig staged under its configuration root, for example through a link made withpig reload --sdk-path gobefore updating pig, now builds against the SDK the current pig staged. pig never deletes the SDK directories older versions staged, so such a link kept resolving and the extension compiled against the old SDK, failing at load withdecode register payload: json: unknown field .... - Root cause: A go.mod replace (or Cargo path dependency) that resolves inside <config root>/state/pigsdk/ but outside the SDK this pig stages now points at an SDK an earlier pig staged; pig never deletes those directories, so a link made with
pig reload --sdk-path gokept resolving after an update and the extension compiled against the old SDK, failing at register withdecode register payload: json: unknown field "can_block"and makingpig extension upgradetype-check against the same old SDK. Treat such a replace like a missing one in the packed, isolated and upgrade-analysis paths, Go and Rust; a replace anywhere else still wins. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d305b76bda(2026-10-12) - Tests:
coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideKeepsCurrentStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestRustSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/subprocess/builder_test.go::TestGoNeedsStagedSDK,coding/extension/host/subprocess/builder_test.go::TestRustNeedsStagedSDK - Release note: A Go extension whose
go.modreplace (or a Rust extension whosepig-sdkpath dependency) points at an SDK an earlier pig staged under its configuration root, for example through a link made withpig reload --sdk-path gobefore updating pig, now builds against the SDK the current pig staged. pig never deletes the SDK directories older versions staged, so such a link kept resolving and the extension compiled against the old SDK, failing at load withdecode register payload: json: unknown field .... An extension written for an older SDK now gets thewritten for an older SDKreport instead. A replace that points anywhere else, such as your own SDK checkout or copy, still wins.
BF-d36b329
The edit tool no longer accepts a JSON-string edits value that starts or ends with non-JSON whitespace such…
- Source: PiG's own defect
- Symptom: The edit tool no longer accepts a JSON-string
editsvalue that starts or ends with non-JSON whitespace such as a vertical tab or a no-break space; Pi'sJSON.parserejects it. - Root cause: prepareEditArguments JSON.parses a string edits (edit.ts:117-127), and JSON.parse accepts only space, tab, LF and CR around the value. strings.TrimSpace also stripped \v, \f, U+0085 and U+00A0, so Pig accepted edits Pi leaves as a string.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d36b329a4c(2026-10-06) - Tests:
internal/codingagent/tools/edit_diff_test.go::TestEditPrepareArgumentsKeepsMemberOrder - Release note: The edit tool no longer accepts a JSON-string
editsvalue that starts or ends with non-JSON whitespace such as a vertical tab or a no-break space; Pi'sJSON.parserejects it.
BF-d37c769
A replacement request body from a before_provider_request hook on Anthropic Messages, and every OpenAI…
- Source: PiG's own defect
- Symptom: A replacement request body from a
before_provider_requesthook on Anthropic Messages, and every OpenAI Codex WebSocketresponse.createmessage, now leave in Pi's member order (typefirst for the WebSocket message) instead of sorted keys. - Root cause: The Anthropic replacement path split the body into a map and the WebSocket path built {type, ...requestBody} from a map, so both wrote sorted keys where Pi's JSON.stringify writes insertion order (type first, then the body's members; a cached continuation replaces input in place and adds previous_response_id last). Both now edit the body member by member (ai/ordered_json_object.go). Byte parity, no divergence.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d37c76932f(2026-10-10) - Tests:
ai/request_body_member_order_test.go::TestAnthropicReplacementBodyKeepsItsMemberOrder,ai/request_body_member_order_test.go::TestCodexWebSocketMessageKeepsTheRequestBodyMemberOrder - Release note: A replacement request body from a
before_provider_requesthook on Anthropic Messages, and every OpenAI Codex WebSocketresponse.createmessage, now leave in Pi's member order (typefirst for the WebSocket message) instead of sorted keys.
BF-d39069d
Tool cards rebuilt when a session is resumed now follow the showImages and imageWidthCells settings
- Source: PiG's own defect
- Symptom: Tool cards rebuilt when a session is resumed now follow the
showImagesandimageWidthCellssettings. - Root cause: Fixes resumed tool cards ignoring terminal.showImages/imageWidthCells (interactive-mode.ts:3997 passes both; renderSessionEntries built the card without them).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d39069da65(2026-10-06) - Tests:
internal/codingagent/interactive_tool_execution_options_test.go::TestResumedToolCardsUseTheImageSettings,internal/codingagent/interactive_tool_execution_options_test.go::TestToolExecutionOptionsDefaultWhenUndefined,internal/imageprocessing/image_processing_upstream_test.go::TestFormatDimensionNoteUpstream,internal/imageprocessing/input_limits_test.go::TestResizeModelProfileJPEGQualityAndSizeOnlyHint - Release note: Tool cards rebuilt when a session is resumed now follow the
showImagesandimageWidthCellssettings.
BF-d3b3618
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
d3b36181ab(2026-10-21) - Tests: None recorded.
BF-d42aebb
ai.ValidateToolArguments and ai.ValidateToolCall list a failed validation's errors in the order the…
- Source: PiG's own defect
- Symptom:
ai.ValidateToolArgumentsandai.ValidateToolCalllist a failed validation's errors in the order the tool's schema declares its properties, as Pi does, instead of sorted by property name. - Root cause: Pi validates tool.parameters itself (validation.ts:317-341), so TypeBox lists property errors in the schema's member order. ValidateToolArguments encoded tool.Parameters with json.Marshal and listed them sorted; it now validates ToolSchema.ParametersJSON.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d42aebb5a0(2026-10-13) - Tests:
ai/validate_schema_order_test.go::TestValidateToolArgumentsListsErrorsInTheSchemasMemberOrder - Release note:
ai.ValidateToolArgumentsandai.ValidateToolCalllist a failed validation's errors in the order the tool's schema declares its properties, as Pi does, instead of sorted by property name.
BF-d449f74
tool_search and the Durable searchTools() ranker compute the same BM25 scores as Pi on arm64: the length…
- Source: PiG's own defect
- Symptom:
tool_searchand the DurablesearchTools()ranker compute the same BM25 scores as Pi on arm64: the length normalizationk1 * (1 - b + b * length / averageLength)is rounded before the term count is added, instead of being fused into one multiply-add, so scores no longer differ from Pi's in the last bit and documents that tie in Pi keep their order (Pi 1.2.0tool-search/tool.tsBm25Ranker,durable/src/search.tsrankDocuments). - Root cause: On arm64 the Go compiler fused k1*(...) + count into one FMADD, so tool_search and searchTools() scores differed from Pi's in the last bit and could reorder ties. TestToolSearchMatchesPi had 65 such mismatches.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34c4d8e058(2026-10-12),d449f7433b(2026-10-13) - Tests:
coding/extension/builtin/toolsearch/toolsearch_oracle_test.go::TestToolSearchMatchesPi,durable/internal/search/search_test.go::TestRankDocumentsRoundsEveryOperationLikeJavaScript - Release note:
tool_searchand the DurablesearchTools()ranker compute the same BM25 scores as Pi on arm64: the length normalizationk1 * (1 - b + b * length / averageLength)is rounded before the term count is added, instead of being fused into one multiply-add, so scores no longer differ from Pi's in the last bit and documents that tie in Pi keep their order (Pi 1.2.0tool-search/tool.tsBm25Ranker,durable/src/search.tsrankDocuments).
BF-d490a8b
Tool argument validation errors follow Pi: at most eight errors are listed (TypeBox's maxErrors), and…
- Source: PiG's own defect
- Symptom: Tool argument validation errors follow Pi: at most eight errors are listed (TypeBox's
maxErrors), and errors for additional properties, pattern properties and property names come in the arguments' member order instead of sorted order. - Root cause: validateToolArguments (packages/ai/src/utils/validation.ts, with typebox 1.3.27 Compile/Value.Convert/Check/Errors from internal/experimental/protocol/testdata/typebox, testdata/validate_tool_args.mjs) runs on 12000 seeded schemas (scalars, type unions, enum, const, bounds, patterns, objects with required/additionalProperties/patternProperties/dependentRequired, arrays, anyOf/oneOf/allOf, defaults) and argument values (right types, stringified numbers and booleans, nulls, wrong shapes, big numbers). The coerced result or the whole error message must match. 118 of 6000 differed before (red): (1) TypeBox's error context keeps the first 8 errors (Settings.maxErrors) and Go listed all; (2)…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d490a8b161(2026-10-09) - Tests:
ai/validate_tool_args_oracle_test.go::TestValidateToolArgumentsMatchesPiOnSeededSchemas - Release note: Tool argument validation errors follow Pi: at most eight errors are listed (TypeBox's
maxErrors), and errors for additional properties, pattern properties and property names come in the arguments' member order instead of sorted order.
BF-d4ddb14
The /scoped-models search input stays editable after a submit key reaches it (LF under the Kitty keyboard…
- Source: PiG's own defect
- Symptom: The
/scoped-modelssearch input stays editable after a submit key reaches it (LF under the Kitty keyboard protocol, or Enter withtui.select.confirmrebound): it was a host-owned input that finished on submit and ignored all later typing; Pi buildsnew Input()with noonSubmit(tui/scoped_models_list.go;TestScopedModelsSelectorMatchesPinow also runs 336 Kitty-protocol probes, red before; regressionTestScopedModelsSearchStaysEditableAfterASubmitKey). - Root cause: The list built its search with NewTextInput (host-owned: submit marks it done and later input is ignored); Pi builds new Input() with no onSubmit. Under the Kitty protocol LF is not select.confirm, so it reached the input and the query froze; the same happens when select.confirm is rebound.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),33fe10d58e(2026-10-11),c4ad739eb8(2026-10-11),d4ddb14da2(2026-10-11),fac470319e(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/models_error_oracle_test.go::TestModelsErrorMessageMatchesPi,coding/extension/host/inproc/registries_test.go::TestShortcuts_DiagnosticMessageMatchesUpstream,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,coding/mcpext/manager_view_oracle_test.go::TestMcpManagerViewMatchesPiand 12 more in docs/bug-ledger.json - Release note: The
/scoped-modelssearch input stays editable after a submit key reaches it (LF under the Kitty keyboard protocol, or Enter withtui.select.confirmrebound): it was a host-owned input that finished on submit and ignored all later typing; Pi buildsnew Input()with noonSubmit(tui/scoped_models_list.go;TestScopedModelsSelectorMatchesPinow also runs 336 Kitty-protocol probes, red before; regressionTestScopedModelsSearchStaysEditableAfterASubmitKey).
BF-d50ec25
The TypeScript extension runtime no longer depends on Node-only behaviour: its entry modules do not await the…
- Source: PiG's own defect
- Symptom: The TypeScript extension runtime no longer depends on Node-only behaviour: its entry modules do not await the admission loop at top level (Bun never reaches
beforeExitwhile an entry module's top-level await is pending, so the quit drain after RPC input ended hung), it hooksv8.Serializer.prototype.writeValueonly where the runtime has it, it keeps a native method when the runtime froze built-ins, and it loads its lazy modules through one list that a bundling runtime can serve. - Root cause: retryOnNextRuntime retried every error that was not a *LoadError, including a provider registration the host rejected after the register handshake; with a slot runtime selected the factory then ran again on the next runtime over its partial registration. It now retries only a *LoadError with a spawn, exit or register failure code, as its comment states. TestTypeScriptRuntimeRetriesOnlyStartFailures failed before the fix (provider registration error case).
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
086bed4866(2026-10-10),3f3ebefabc(2026-10-10),5a2b6b9941(2026-10-10),9bc13c0910(2026-10-10),d50ec25dee(2026-10-10) - Tests:
coding/cli/startup_alloc_budget_test.go::TestStartupAllocationBudget,coding/cli/startup_init_budget_test.go::TestLinkedPackagesCompileNoRegexpAtInit,coding/cli/startup_init_budget_test.go::TestPackageInitWorkBudget,coding/extension/host/subprocess/node_cell_failure_test.go::TestNodeLauncherServeRejectionExitsOne,coding/extension/host/subprocess/node_jiti_cache_test.go::TestNodeJitiCachePersistsWithoutStaleSource,coding/extension/host/subprocess/runtime_modules_test.go::TestRuntimeModulesListsEveryLazyLoad,coding/extension/host/subprocess/ts_runtime_test.go::TestTypeScriptRuntimeRetriesAFailedStartOnTheNextRuntime,coding/extension/host/subprocess/ts_runtime_test.go::TestTypeScriptRuntimeRetriesOnlyStartFailuresand 1 more in docs/bug-ledger.json - Release note: The TypeScript extension runtime no longer depends on Node-only behaviour: its entry modules do not await the admission loop at top level (Bun never reaches
beforeExitwhile an entry module's top-level await is pending, so the quit drain after RPC input ended hung), it hooksv8.Serializer.prototype.writeValueonly where the runtime has it, it keeps a native method when the runtime froze built-ins, and it loads its lazy modules through one list that a bundling runtime can serve. On Node, a serve failure still ends the extension process with exit code 1; a runtime process whose event loop empties while it waits for the next admission now exits 0 instead of Node's unsettled-top-level-await code 13 with its warning.
BF-d514b3d
In a Node extension's before_agent_start handler, event.systemPrompt and ctx.getSystemPrompt() now…
- Source: PiG's own defect
- Symptom: In a Node extension's
before_agent_starthandler,event.systemPromptandctx.getSystemPrompt()now render the current options, as in Pi. - Root cause: The Node runtime gave event.systemPrompt as the string the host rendered before the handler ran, and ctx.getSystemPrompt() returned the Session's prompt.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d514b3de97(2026-10-13) - Tests:
coding/extension/host/inproc/before_agent_start_render_test.go::TestBeforeAgentStartPromptFollowsEveryOptionsEdit,coding/extension/host/subprocess/render_system_prompt_test.go::TestUIBridgeRenderSystemPromptRendersTheOptionsItIsSent,test/parity/closure/known_gaps_030_test.go::TestApproved030GapsRemainProvisionalNotWaived,test/parity/scenarios/extensions-runtime/82-before-agent-start-current-system-prompt.toml - Release note: In a Node extension's
before_agent_starthandler,event.systemPromptandctx.getSystemPrompt()now render the current options, as in Pi. The handler's own edits toevent.systemPromptOptionsand an earlier handler's returnedsystemPromptboth show.
BF-d53cd09
A tool call whose arguments are not an object is written back to each provider as Pi writes it: Anthropic and…
- Source: PiG's own defect
- Symptom: A tool call whose arguments are not an object is written back to each provider as Pi writes it: Anthropic and Google send
{}fornull(arguments ?? {}), Mistral sends{}fornull,0,falseand""(arguments || {}), Bedrock sends the value itself; a Gemini function call with"args": nullis the empty object, and the agent proxy keeps Pi'sparseStreamingJson(...) || {}and the streamed arguments of atoolcall_endthat omits them. - Root cause: An earlier change keeps a non-object parse result ([] null 5 "s") as Pi's parseStreamingJson returns it. The sites that read or write the value then passed it through verbatim, where Pi applies its own
?? {}or|| {}. For null that is a regression: before the commit every site wrote {}. - Google ingest: google-generative-ai.ts:207args ?? {}."args": nullnow became the arguments null with delta "null". It is {} again (defaultNullishArguments). - Replay: anthropic-messages.ts:1444input: arguments ?? {}and google-shared.ts:270args: arguments ?? {}sent null. mistral-conversations.ts:849JSON.stringify(arguments || {})sent "null", "0", "false" and"".… - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d53cd09a75(2026-10-10) - Tests:
agent/proxy_non_object_arguments_test.go::TestProxyNonObjectToolArgumentsFollowPi,ai/tool_call_non_object_converters_test.go::TestGoogleNullFunctionCallArgsAreTheEmptyObject,ai/tool_call_non_object_converters_test.go::TestNonObjectToolArgumentsReplayAsEachProviderDoes - Release note: A tool call whose arguments are not an object is written back to each provider as Pi writes it: Anthropic and Google send
{}fornull(arguments ?? {}), Mistral sends{}fornull,0,falseand""(arguments || {}), Bedrock sends the value itself; a Gemini function call with"args": nullis the empty object, and the agent proxy keeps Pi'sparseStreamingJson(...) || {}and the streamed arguments of atoolcall_endthat omits them.
BF-d60d422
pi.exec gives the command PiG's environment unchanged, as Pi does, so a command started in a symlinked…
- Source: PiG's own defect
- Symptom:
pi.execgives the command PiG's environment unchanged, as Pi does, so a command started in a symlinked directory no longer gets that directory as itsPWD;pwdthere now prints the physical directory, as in Pi. - Root cause: os/exec sets PWD to cmd.Dir when cmd.Env is nil; Node's spawn without an env option passes process.env unchanged (normalizeSpawnArguments). pi.exec's child in a symlinked cwd (macOS /var -> /private/var) then printed the symlinked name from pwd where Pi prints the physical directory (TestExecCommandMatchesPi scenarios 11 and 48).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),d60d422e2e(2026-10-13) - Tests:
coding/extension/exec_oracle_test.go::TestExecCommandMatchesPi,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,internal/nodespawn/start_other_test.go::TestStartWithoutEnvPassesPiGsEnvironmentAsNodeDoes,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.toml,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-switch.tomland 1 more in docs/bug-ledger.json - Release note:
pi.execgives the command PiG's environment unchanged, as Pi does, so a command started in a symlinked directory no longer gets that directory as itsPWD;pwdthere now prints the physical directory, as in Pi.
BF-d64f8ed
durable.StorageRequestError and durable.SessionFailed are Pi's error classes of the same names…
- Source: PiG's own defect
- Symptom:
durable.StorageRequestErroranddurable.SessionFailedare Pi's error classes of the same names (errors.ts). - Root cause: The built-in storages return a
StorageRequestErrorfor a read of an unknown conversation, a document without retained history, or a cursor or scan order they did not write; the driver matches both names in a remote error. - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f1d3eec6de(2026-10-09),d64f8edf71(2026-10-10) - Tests:
ai/anthropic_microtask_trace_test.go::TestAnthropicMicrotaskTrace,ai/credential_expiry_test.go::TestCredentialExpiryMatchesPi,ai/zen_upstream_test.go::TestOpenCodeModelsSmokeUpstream,durable/core/driver/remote_errors_session_test.go::TestCoreErrorMatchesStorageRequestErrorAndSessionFailed,durable/errors_session_failed_test.go::TestStorageRequestErrorAndSessionFailedMatchErrorsTS,durable/storage/storage_request_error_test.go::TestMemoryStorageReadsRejectInvalidRequestsWithStorageRequestError,test/parity/runner/binaries_missing_test.go::TestResolveUpstreamPiBinNeverFallsBackToAMiseInstallWithoutOptIn - Release note:
durable.StorageRequestErroranddurable.SessionFailedare Pi's error classes of the same names (errors.ts). The built-in storages return aStorageRequestErrorfor a read of an unknown conversation, a document without retained history, or a cursor or scan order they did not write; the driver matches both names in a remote error.
BF-d6aaf72
A setTimeout(0) or setImmediate callback that an extension queues while an RPC stdin command runs (a…
- Source: PiG's own defect
- Symptom: A call that a Go, Rust or Python extension makes through a context whose request returned runs on arrival while another request of the extension is in flight, instead of waiting for a loop turn that waits for that request: a handler that kept an earlier request's context and awaited a call through it used to hang the run.
- Root cause: A handler that is still awaiting real I/O (a child process, a dialog) is not held back, because Pi's loop turns while it waits and runs the timer.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
463879414a(2026-10-21),58563ba284(2026-10-21),5a182154fc(2026-10-21),7a4ddb3733(2026-10-21),8506081bd3(2026-10-21),d6aaf72307(2026-10-21) - Tests:
extensions/sdk/request_lifetime_race_test.go::TestNormalCompletionPublishedBeforeResponseIsVisible,extensions/sdk/request_lifetime_race_test.go::TestRetainedContextParentSelectionRacesNormalResponse,internal/jsloop/turns_test.go::TestReleaseRunsOnlyTheCallbacksHeldForItsOwner,test/extension-conformance/timer_calls_test.go::TestHostCallAwaitedByARunningHandlerIsNotHeldForTheLoopTurn,test/parity/scenarios/rpc/70-rpc-command-chain-timer-send.toml,test/parity/scenarios/rpc/71-rpc-command-chain-timer-dialog.toml - Release note: A
setTimeout(0)orsetImmediatecallback that an extension queues while an RPC stdin command runs (amodel_selecthandler duringset_model, say) now reaches the client after the command's response, as in Pi, instead of before it in about one run in ten. Pi runs the command's microtasks and writes its response before its loop runs a timer, so the Node runtime now holds its timer callbacks from the moment the host reads the command until the host has written the response, and the host waits for the runtime's acknowledgement of that hold before it sends the command's first event. A handler that is still awaiting real I/O (a child process, a dialog) is not held back, because Pi's loop turns while it waits and runs the timer. A call that a Go, Rust or Python extension makes through a context whose request returned runs on arrival while another request of the extension is in flight, instead of waiting for a loop turn that waits for that request: a handler that kept an earlier request's context and awaited a call through it used to hang the run.
BF-d6e0dca
The extension SDK surface matrix and the extension API pages say that setEditorComponent and…
- Source: PiG's own defect
- Symptom: The extension SDK surface matrix and the extension API pages say that
setEditorComponentandgetEditorComponentexist in the Go, Rust and Python SDKs and that the getter returns the factory the extension installed. - Root cause: The matrix rows and the exception are corrected and the getter is documented for every SDK. Signed-off-by: Michael Kinsy mrkinsy5@gmail.com
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d6e0dca953(2026-10-09) - Tests:
automation/ci/bugledger/bugledger_test.go::TestEveryNewDivergenceIsAccountedFor - Release note: The extension SDK surface matrix and the extension API pages say that
setEditorComponentandgetEditorComponentexist in the Go, Rust and Python SDKs and that the getter returns the factory the extension installed. They described the Go getter as always returning nil and the Rust and Python getters as missing.
BF-d7855fd
A subprocess extension's message, entry or tool renderer, or tool renderer resolver, that was busy past the…
- Source: PiG's own defect
- Symptom: A subprocess extension's message, entry or tool renderer, or tool renderer resolver, that was busy past the five-second renderer inactivity boundary no longer leaves its card on the fallback until the next resize: the host asks again once the extension answers the abandoned request, and an extension that keeps missing the boundary is asked again after a backoff that starts at five seconds and doubles to one minute.
- Root cause: A resolve_tool_renderers request the renderer inactivity boundary abandoned recorded next() as the extension's answer, so the tool drew without the resolver's renderers for the rest of the session; Pi's resolvers answer synchronously. An abandoned resolution now records nothing, and renderRetry asks the extension again with the same next() (on the late answer after the first abandonment in a row, or after the backoff), on the goroutine group Shutdown joins. As for the first request, only an answer that differs from next() draws the tool's cards again. awaitRenderRetry also ends without a retry when the connection closes first.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
84e70fc701(2026-10-13),d7855fd99b(2026-10-14) - Tests:
coding/extension/host/subprocess/render_retry_backoff_test.go::TestRenderRetryBackoffDoublesUpToItsCap,coding/extension/host/subprocess/render_retry_test.go::TestRenderProxyBacksOffFromARendererThatAlwaysAnswersLate,coding/extension/host/subprocess/render_retry_test.go::TestRenderProxyBacksOffFromAnExtensionThatNeverAnswers,coding/extension/host/subprocess/render_retry_test.go::TestRenderProxyRendersAgainOnceTheExtensionAnswersAnAbandonedRender,coding/extension/host/subprocess/render_retry_test.go::TestToolRendererResolutionAsksAgainAfterAnAbandonedRequest,test/extension-conformance/tool_renderer_resolvers_test.go::TestToolRendererRegisteredAfterLoadingAcrossSDKs,test/extension-conformance/tool_renderer_resolvers_test.go::TestToolRendererResolversAcrossSDKs - Release note: A subprocess extension's message, entry or tool renderer, or tool renderer resolver, that was busy past the five-second renderer inactivity boundary no longer leaves its card on the fallback until the next resize: the host asks again once the extension answers the abandoned request, and an extension that keeps missing the boundary is asked again after a backoff that starts at five seconds and doubles to one minute.
BF-d7bfea6
A git source whose ref has no repository (git:#v1) no longer panics, and a ref split out of an opaque-path…
- Source: PiG's own defect
- Symptom: A git source whose ref has no repository (
git:#v1) no longer panics, and a ref split out of an opaque-path URL (git:bitbucket:git+https://@v1) leaves the repository whole, as Pi'sparseGitUrldoes. - Root cause: A seeded random corpus (6,000 strings from the tokens parseGitUrl branches on) added to TestParseGitMatchesPi found two differences: - hostedgit.go isGitHubShorthand read arg[firstHash-1] when the argument starts with '#', which JavaScript reads as undefined (not '/') and Go panicked on ("git:#v1" gives the hosted candidate "#v1"). - nodeurl.URL.WithPathname replaced the path of a URL with an opaque path ("bitbucket:git+https://@v1"), where the pathname setter does nothing; splitRef then returned a rewritten repository and Pi returns the URL whole.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d7bfea6f68(2026-10-11) - Tests:
coding/source/parse_git_oracle_test.go::TestParseGitMatchesPi,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode - Release note: A git source whose ref has no repository (
git:#v1) no longer panics, and a ref split out of an opaque-path URL (git:bitbucket:git+https://@v1) leaves the repository whole, as Pi'sparseGitUrldoes.
BF-d7c7f86
fix(runtimecell): start a packed cell's members in order so a later member's factory cannot take the factory…
- Source: PiG's own defect
- Symptom: fix(runtimecell): start a packed cell's members in order so a later member's factory cannot take the factory turn from an earlier one
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
d7c7f86a12(2026-10-21) - Tests: None recorded.
BF-d80425c
fix(train): gate repairs after merging train 3 and the ordering stack
- Source: PiG's own defect
- Symptom: fix(train): gate repairs after merging train 3 and the ordering stack
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
d80425c2e2(2026-10-21) - Tests: None recorded.
BF-d80c1cd
Where Pi calls new URL(value), Pig now parses a ws:, wss:, ftp: or file: URL, and a bracketed IPv6…
- Source: PiG's own defect
- Symptom: Where Pi calls
new URL(value), Pig now parses aws:,wss:,ftp:orfile:URL, and a bracketed IPv6 host of any other scheme, instead of refusing it. - Root cause: ParseURL (genericurl.go) now also parses ws, wss and ftp through the WHATWG special parser (default ports 80, 443 and 21, shared with http and https via parseSpecialHref), file through the file URL parser (host, path, query, fragment), and a bracketed IPv6 host of a non-special scheme through opaqueHost, and reports Query next to Protocol, Username, Password, Hostname, Pathname and Hash. url.go (AnySchemeURL, ParseAnyScheme, parseNonSpecialAuthority) is removed; opaqueHost moved into genericurl.go and isForbiddenHostCodePoint, now duplicated by it, is gone. Callers updated: ai urlHostname (github-copilot enterprise domain) and authorizationURLQuery (pasted OAuth input) use ParseURL…
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
056bbfb74e(2026-10-09),15a5c0b670(2026-10-09),23aa78b173(2026-10-09),2c04c1445a(2026-10-09),7c4d874438(2026-10-09),97e178f7f6(2026-10-09),c4d51d5c1a(2026-10-09),d80c1cd150(2026-10-09),da99854d1b(2026-10-09) - Tests:
ai/authorization_input_oracle_test.go::TestAuthorizationInputParsersMatchPi,ai/codex_account_id_oracle_test.go::TestCodexAccountIDMatchesPi,ai/githubcopilot_oracle_test.go::TestGitHubCopilotHelpersMatchPi,ai/streaming_json_oracle_test.go::TestStreamingJSONMatchesPiOnSeededInput,ai/trusted_url_oracle_test.go::TestTrustedVerificationURLsMatchPi,coding/cli/strip_app_features_test.go::TestStripExportHTMLBuildOmitsExportPackage,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode,internal/nodeurl/url_oracle_test.go::TestParseMatchesNodeURLand 1 more in docs/bug-ledger.json - Release note: Where Pi calls
new URL(value), Pig now parses aws:,wss:,ftp:orfile:URL, and a bracketed IPv6 host of any other scheme, instead of refusing it. This affects package and repository sources (coding/source), the OAuth paste and GitHub Copilot enterprise-domain inputs.
BF-d8357e2
Cache warmer pure functions against live Pi: new TestCacheWarmerPureFunctionsMatchPi…
- Source: PiG's own defect
- Symptom: Cache warmer pure functions against live Pi: new
TestCacheWarmerPureFunctionsMatchPi(internal/codingagent/cache_warmer_format_oracle_test.go;getCacheWarmingDelayMs,getPromptCacheTtlMs,isReplayable,formatCacheWarmingStatus,formatCacheWarmingUsage; 6,000 inputs under three processPI_CACHE_RETENTIONvalues; TTLs around the 10 s margin, tier values 0/fractional, empty request env falling through to the process one, adaptive vs budget thinking, every status state with rounding-boundary probabilities and dollars). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
d8357e2802(2026-10-11) - Tests:
internal/codingagent/cache_warmer_format_oracle_test.go::TestCacheWarmerPureFunctionsMatchPi - Release note: Cache warmer pure functions against live Pi: new
TestCacheWarmerPureFunctionsMatchPi(internal/codingagent/cache_warmer_format_oracle_test.go;getCacheWarmingDelayMs,getPromptCacheTtlMs,isReplayable,formatCacheWarmingStatus,formatCacheWarmingUsage; 6,000 inputs under three processPI_CACHE_RETENTIONvalues; TTLs around the 10 s margin, tier values 0/fractional, empty request env falling through to the process one, adaptive vs budget thinking, every status state with rounding-boundary probabilities and dollars). No bug (formatCacheWarmingStatus was previously only reached through the /session oracle). 19 mutations red; 5 equivalent: empty-API guard inIsReplayable,Phase/Actionclosed-union comparisons, theseconds > 0clause when no larger unit printed (nextWarmAt > nowforces at least 1 s), and thenoneretention check (a model'spromptCacheholds only short/long). Not compared: NaN/infinite TTLs.
BF-d87b70c
A second ctx.ui.custom component opened while one is shown now opens over it at once, as in Pi, whether it…
- Source: PiG's own defect
- Symptom: A subprocess extension's custom component waited until the shown one closed, and a subprocess custom component kept reading the keys while another custom component shown over it had focus.
- Root cause: Each SDK now holds the prompt the host rendered for the options the handler received and, once the options differ, asks the host to render them with renderSystemPrompt, reusing the result until they change again: - Go: data["systemPrompt"] is an *sdk.SystemPrompt (Get, MarshalJSON), since a map cannot run a getter; ctx.GetSystemPrompt() returns the same value. - Python: the event dict's "systemPrompt" and ctx.get_system_prompt() render. The Host answers renderSystemPrompt without a UI bridge too (an SDK session): the render is a pure function of the options the call carries. Callers of the Go string form move to Get(); the known gaps are deleted; pig-go.json is regenerated by gointerfaces.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),007c91dcbb(2026-10-13),1848633092(2026-10-13),2a055a7a3a(2026-10-13),588884c3e8(2026-10-13),d87b70c54c(2026-10-13) - Tests:
coding/extension/host/inproc/specialized_dispatch_test.go::TestEmitBeforeAgentStart_ForcedPromptWinsAndInvalidSectionFailsTheHandler,coding/extension/host/subprocess/host_calls_test.go::TestReadLoopWidgetPushOrdersAgainstQueuedSlotCalls,coding/extension/host/subprocess/ui_bridge_test.go::TestFocusedCustomComponentsStack,coding/extension/host/subprocess/ui_bridge_test.go::TestInteractiveDialogOpensOverFocusedOverlay,coding/extension/host/subprocess/ui_prompt_test.go::TestUIBridgePromptScopeDialogOverCustomAndNoUI,coding/extension/host/subprocess/view_terminal_parity_test.go::TestViewsLeaveTheTerminalBytesUnchanged,coding/extension/host/subprocess/widget_key_namespace_test.go::TestTwoExtensionsSettingOneWidgetKeyShareIt,extensions/sdk-py/tests/test_system_prompt_render.pyand 21 more in docs/bug-ledger.json - Release note: A second
ctx.ui.customcomponent opened while one is shown now opens over it at once, as in Pi, whether it is an overlay or not and whichever extension process opens it. The component on top takes focus and the keys, and when it closes focus returns to the component below, which receives the next key. Before, a subprocess extension's custom component waited until the shown one closed, and a subprocess custom component kept reading the keys while another custom component shown over it had focus.
BF-d88dca7
harness.ToolTaskInput.Kind and ToolTaskResult.Kind are the named string type harness.ToolTaskKind with…
- Source: PiG's own defect
- Symptom:
harness.ToolTaskInput.KindandToolTaskResult.Kindare the named string typeharness.ToolTaskKindwithToolTaskModelandToolTaskNested, andharness.ToolHookCallcarriesType("toolCall") like Pi'sToolHookCall; build one withharness.NewToolHookCall. - Root cause: .upstream/pin names the pinned Pi build: a release tag or a pi-mono commit (version 1.1.0+). pigversion.go is generated from it; coding gains UpstreamRef and UpstreamPackageVersion beside the display version. PIUPSTREAM_REF=<tag|commit> overrides the pin for one run (make variables, a pigversion.go build overlay, PIG_UPSTREAM* in the environment). make pin-bump moves the pin and regenerates what derives from it: SDK, CI-image and Durable package pins, README and ledger version fields, the source mirror, the inventories.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
86fe3dc28b(2026-10-09),f1d3eec6de(2026-10-09),014c014e74(2026-10-10),0a25e09319(2026-10-10),2de6fe21b8(2026-10-10),69538c209e(2026-10-10),76d1a85e45(2026-10-10),90b48897e1(2026-10-10),b7199a2ba1(2026-10-10),c87dea7287(2026-10-10),d88dca723b(2026-10-10) - Tests:
ai/validate_value_test.go::TestCheckSchemaValue,automation/pin/test_pin.py,durable/core/docs/addtools_test.go::TestAddToolsExtendsModelToolsAsWell,durable/core/driver/tool_structured_test.go::TestRetainedOutputIsTheOutputKeptSoFar,durable/core/driver/tool_structured_test.go::TestRetainedOutputReportsDroppedOutputAsTruncated,durable/core/turn/generation_test.go::TestToolThrowEndsFailedWithTheErrorInTheResult,durable/core/turn/tool_v2_test.go::TestToolTaskMigratesVersion1,durable/core/turn/tool_v2_test.go::TestToolTaskRecordIsAVersion2ModelCalland 33 more in docs/bug-ledger.json - Release note:
harness.ToolTaskInput.KindandToolTaskResult.Kindare the named string typeharness.ToolTaskKindwithToolTaskModelandToolTaskNested, andharness.ToolHookCallcarriesType("toolCall") like Pi'sToolHookCall; build one withharness.NewToolHookCall.
BF-d8bed22
auth.json keeps every provider's entry as written, as Pi does
- Source: PiG's own defect
- Symptom:
auth.jsonkeeps every provider's entry as written, as Pi does. - Root cause: TestAuthStorageOnCorruptedFileMatchesPi runs 800 scripts (create, list, read, modify, delete, file rewritten between calls) over corrupted auth.json files against the installed AuthStorage: 293 differed. A wrong-typed field in one entry ("key": 5, "env": {"A": 1}) made every read, list and write fail with 'auth: parse: ...' (Pi keeps the value and the other providers work), and an incomplete entry ({"type":"oauth"}, {}) was rewritten into the typed form (empty refresh/access, "type": "") by an unrelated write. An entry now keeps its source when the typed credential cannot hold it exactly (decodeStoredCredential), written canonically unless the caller changed it (sourceUnchanged). Legacy PiG…
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1a29f511e7(2026-10-09),2967084da2(2026-10-09),81e0e1b24e(2026-10-09),d07038cb99(2026-10-09),ea72139272(2026-10-09),d8bed22101(2026-10-10) - Tests:
ai/auth_file_bytes_oracle_test.go::TestAuthFileBytesMatchPi,ai/auth_load_oracle_test.go::TestAuthStorageOnCorruptedFileMatchesPi,ai/auth_opaque_entries_test.go::TestAuthStorageKeepsEntriesItCannotTypeAcrossWrites,ai/classifier_answers_oracle_test.go::TestClassifierAnswersMatchPi,ai/oauth_credential_members_test.go::TestOAuthBuildersWriteCredentialMembersInPiOrder,ai/system_one_shared_test.go::TestSystemOneSharedFailureMessages,internal/codingagent/migrations_tree_oracle_test.go::TestRunMigrationsTreeMatchesPi - Release note:
auth.jsonkeeps every provider's entry as written, as Pi does. An entry the credential types cannot hold (a wrong-typed field such as"key": 5or"env": {"A": 1}, an OAuth entry withoutrefreshoraccess, a value that is not an object) no longer makes every credential read, list and write fail withauth: parse: …, and an unrelated login or logout no longer rewrites that entry into the typed form ("type": "", emptyrefreshandaccess).
BF-d8d513a
With the Bun TypeScript runtime (PIG_TS_RUNTIME=bun), /reload now evaluates an edited ES module extension…
- Source: PiG's own defect
- Symptom: Bun kept the first evaluation until PiG restarted.
- Root cause: Lead ruling 2026-10-11T20:57Z (2b): port D93 to the Bun runtime so Node and Bun behave the same.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d8d513a98a(2026-10-11) - Tests:
coding/extension/host/subprocess/runtime_modules_test.go::TestRuntimeModulesListsEveryLazyLoad,coding/extension/host/subprocess/xref_event_bus_test.go::TestXrefEventBusCloneLocalGraphsMatchPi,coding/extension/host/subprocess/xref_event_bus_test.go::TestXrefEventBusCloneMatchesPi,coding/extension/host/subprocess/xref_event_bus_test.go::TestXrefEventBusInspectMatchesPi,coding/extension/host/subprocess/xref_event_bus_test.go::TestXrefEventBusIntegrityLevelsMatchPi,coding/extension/host/subprocess/xref_event_bus_test.go::TestXrefEventBusRejectedWritesMatchPi - Release note: With the Bun TypeScript runtime (
PIG_TS_RUNTIME=bun),/reloadnow evaluates an edited ES module extension (.mjs, or.jsunder"type": "module") and the extension modules that import it again, as it does on Node (D93). Before, Bun kept the first evaluation until PiG restarted.
BF-d90c2f4
Markdown now nests a **strong** run inside *emphasis* as Pi does, renders a line underlined with === or…
- Source: PiG's own defect
- Symptom: Markdown now nests a
**strong**run inside*emphasis*as Pi does, renders a line underlined with===or---as a heading, and leaves a blank row between a paragraph and a blockquote, heading, rule or math block that directly follows it. - Root cause: Markdown renders a paragraph underlined with === or --- as a heading, follows marked's setext and emphasis delimiter rules, nests a closed strong run inside emphasis, and spaces a paragraph from the block that interrupts it, as marked and markdown.ts do. Expected bytes come from the pinned pi-tui sources run under Node.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
cf7ad2b7a4(2026-10-06),d90c2f41b7(2026-10-08) - Tests:
internal/codingagent/mermaid_interactive_test.go::TestAssistantThinkingUsesThinkingTransformContext,internal/codingagent/mermaid_interactive_test.go::TestInteractiveUserMessageRunsBuiltinMarkdownTransform,internal/codingagent/mermaid_strip_test.go::TestRuntimeStripMermaidLeavesFenceRaw,internal/codingagent/mermaid_strip_test.go::TestStripMermaidBuildOmitsMermaidRenderer,internal/wordsegmenter/strip_test.go::TestRuntimeStripWordDictionariesKeepsRunsWhole,internal/wordsegmenter/strip_test.go::TestStripWordDictionariesBuildOmitsDictionaryEmbeds,tui/components_test.go::TestEditor_WordBoundaryHelpers,tui/highlight_strip_test.go::TestRuntimeStripSyntaxHighlightRendersUnknownLanguagePathand 5 more in docs/bug-ledger.json - Release note: Markdown now nests a
**strong**run inside*emphasis*as Pi does, renders a line underlined with===or---as a heading, and leaves a blank row between a paragraph and a blockquote, heading, rule or math block that directly follows it.
BF-d983b74
Anthropic requests whose strict prefer tools exceed a strict tool limit ("The compiled grammar is too…
- Source: PiG's own defect
- Symptom: Anthropic requests whose strict
prefertools exceed a strict tool limit ("The compiled grammar is too large", "Too many strict tools", too many union parameters) no longer fail every turn: PiG retries once with theprefertools non-strict, keepsrequiretools strict, and records ananthropic_strict_tools_fallbackdiagnostic, as Pi 1.2.0 does (pi-mono #10409). - Root cause: anthropic-messages.ts sends prefer tools with strict: true and has no fallback for 400 'compiled grammar is too large'. Retry once non-strict unless a tool requires strict.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
d983b74821(2026-10-09) - Tests: None recorded.
- Release note: Anthropic requests whose strict
prefertools exceed a strict tool limit ("The compiled grammar is too large", "Too many strict tools", too many union parameters) no longer fail every turn: PiG retries once with theprefertools non-strict, keepsrequiretools strict, and records ananthropic_strict_tools_fallbackdiagnostic, as Pi 1.2.0 does (pi-mono #10409).
BF-d9886fe
fix(hljs): no magic-literal hits for the resume and dispatch bounds
- Source: PiG's own defect
- Symptom: fix(hljs): no magic-literal hits for the resume and dispatch bounds
- Root cause: maxCheckpointIterations derives from highlightJSIterationLimit (upstream: highlight.min.js iterations; the thrown-error check uses it too). The dispatch mask bound is the bits of its uint64. The resume margin is reach.lines+1, derived instead of a slack constant: an attempt that started on or before the point's line and crossed at most reach.lines line breaks ends before the text does when reach.lines+1 breaks follow the point.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
d9886fe4fa(2026-10-11) - Tests:
tui/internal/hljs/resume_test.go::TestResumeLimit
BF-da5d32b
fix(extension): theme palette reports the theme's own sourcePath; conformance rows for the rest of…
- Source: PiG's own defect
- Symptom: fix(extension): theme palette reports the theme's own sourcePath; conformance rows for the rest of ctx.ui.theme
- Root cause: ExtensionThemePalette took sourcePath only from the active registry's record for the theme's name, so a Theme object no registry holds (ctx.ui.setTheme takes one) reported none, in every SDK. Theme.SourcePath is the field Pi's createTheme sets, so it wins; the registry record stays as the fallback. 30 matrix cells closed.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
da5d32b050(2026-10-10) - Tests:
internal/codingagent/ext_ui_context_test.go::TestExtensionThemePaletteReportsTheThemesOwnSourcePath,test/extension-conformance/theme_test.go::TestThemeQueriesAcrossSDKs
BF-da8a870
Pasting a ChatGPT sign-in callback URL with a ws:, wss: or ftp: scheme, a backslash path or an unusual…
- Source: PiG's own defect
- Symptom: Pasting a ChatGPT sign-in callback URL with a
ws:,wss:orftp:scheme, a backslash path or an unusual host gives Pi's message: "Paste the full callback URL from the browser" only wherenew URLrejects the text, and "The pasted callback URL must start with ..." otherwise. - Root cause: authorizationResultFromManualInput, authorizationResultFromCallback and REDIRECT_URI are cut out of Pi's openai-chatgpt.ts (testdata/chatgpt_manual_input.mjs) and run on 4511 seeded pasted inputs (callback URLs with every shape of code/state/client_id/error, other origins, ports, schemes, relative and non-URL text, JS-whitespace padding). Pig's hybrid (ParseHTTPURL plus a net/url error classifier) differed on every ws:, wss: and ftp: paste (rejected by the first parse, then classified by Go's url.Parse): Pi says 'The pasted callback URL must start with ...' for those and Pig said 'Paste the full callback URL' whenever net/url disagreed with the WHATWG parser.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0b4e1e3376(2026-10-09),15a5c0b670(2026-10-09),da8a8704c6(2026-10-09) - Tests:
ai/chatgpt_manual_input_oracle_test.go::TestChatGPTManualInputMatchesPiOnSeededProbes,ai/codex_vertex_urls_oracle_test.go::TestCodexWebSocketAndVertexVersionURLsMatchPi,ai/trusted_url_oracle_test.go::TestTrustedVerificationURLsMatchPi,internal/nodeurl/genericurl_oracle_test.go::TestParseURLMatchesNode - Release note: Pasting a ChatGPT sign-in callback URL with a
ws:,wss:orftp:scheme, a backslash path or an unusual host gives Pi's message: "Paste the full callback URL from the browser" only wherenew URLrejects the text, and "The pasted callback URL must start with ..." otherwise.
BF-da907ca
The Go program in the SDK page now compiles
- Source: PiG's own defect
- Symptom: The Go program in the SDK page now compiles.
- Root cause: Each entry has symptom, root cause, user impact, platform, fix commits, guarding tests and a source class (PiG's own defect, Pi defect fixed ahead of Pi with its divergence number, Pi issue and remove-when, or a Pi fix ported). The check fails when a fix has no entry, a fix-titled commit that changes shipped code is neither an entry nor listed in exempt.txt, an entry names a missing test, or the committed ledger is stale. Output: docs/bug-ledger.json and docs/site/docs/bug-ledger.md. D110 and D111 gain a "Pi bug:" line, which marks them as fixed ahead of Pi.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
da907cab0b(2026-10-09) - Tests:
automation/ci/bugledger/bugledger_test.go::TestBuildLedgerFromHistory,automation/ci/bugledger/bugledger_test.go::TestBuildNeedsTheReleaseCommit,automation/ci/bugledger/bugledger_test.go::TestExplicitFixMarkerNamesTheFixCommit,automation/ci/bugledger/bugledger_test.go::TestFixedBulletsReadOnlyTheFixedSectionOfLaterReleases,automation/ci/bugledger/bugledger_test.go::TestImpactAndPlatformComeFromTheWording,automation/ci/bugledger/bugledger_test.go::TestPublicTextDropsPrivateSentencesVersionsAndHashes,automation/ci/bugledger/bugledger_test.go::TestReleaseNotesDraftComesFromTheLedgers,automation/ci/bugledger/bugledger_test.go::TestRootCausePrefersALabelThenCauseWordsThenTheFirstParagraphand 39 more in docs/bug-ledger.json - Release note: The Go program in the SDK page now compiles. It called
coding.NewServicesandcoding.ServicesOptions, which do not exist, and the page namedcoding.Servicesforcoding.AgentSessionServices. The Go quickstart extension in the extension API page now compiles as well: its tool schema is ansdk.Schemamap and itssession_starthandler returns(any, error).
BF-da907ca-2
The bundled command page (pig docs show commands) and the website's CLI reference now agree with `pig…
- Source: PiG's own defect
- Symptom: The bundled command page (
pig docs show commands) and the website's CLI reference now agree withpig --help. - Root cause: Each entry has symptom, root cause, user impact, platform, fix commits, guarding tests and a source class (PiG's own defect, Pi defect fixed ahead of Pi with its divergence number, Pi issue and remove-when, or a Pi fix ported). The check fails when a fix has no entry, a fix-titled commit that changes shipped code is neither an entry nor listed in exempt.txt, an entry names a missing test, or the committed ledger is stale. Output: docs/bug-ledger.json and docs/site/docs/bug-ledger.md. D110 and D111 gain a "Pi bug:" line, which marks them as fixed ahead of Pi.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
da907cab0b(2026-10-09) - Tests:
automation/ci/bugledger/bugledger_test.go::TestBuildLedgerFromHistory,automation/ci/bugledger/bugledger_test.go::TestBuildNeedsTheReleaseCommit,automation/ci/bugledger/bugledger_test.go::TestExplicitFixMarkerNamesTheFixCommit,automation/ci/bugledger/bugledger_test.go::TestFixedBulletsReadOnlyTheFixedSectionOfLaterReleases,automation/ci/bugledger/bugledger_test.go::TestImpactAndPlatformComeFromTheWording,automation/ci/bugledger/bugledger_test.go::TestPublicTextDropsPrivateSentencesVersionsAndHashes,automation/ci/bugledger/bugledger_test.go::TestReleaseNotesDraftComesFromTheLedgers,automation/ci/bugledger/bugledger_test.go::TestRootCausePrefersALabelThenCauseWordsThenTheFirstParagraphand 39 more in docs/bug-ledger.json - Release note: The bundled command page (
pig docs show commands) and the website's CLI reference now agree withpig --help. The bundled page lackedpig mcp,pig piglet prune,pig extensions cache prune --failuresand the+nameand-nameforms of--tools; the website page omittedpig piglet pull,updateandpublishfrom its Piglet verbs.
BF-da907ca-3
pig docs show divergences lists D110 and D111, and the README names the pinned Pi release (1.1.0) and its…
- Source: PiG's own defect
- Symptom:
pig docs show divergenceslists D110 and D111, and the README names the pinned Pi release (1.1.0) and its commit. - Root cause: Each entry has symptom, root cause, user impact, platform, fix commits, guarding tests and a source class (PiG's own defect, Pi defect fixed ahead of Pi with its divergence number, Pi issue and remove-when, or a Pi fix ported). The check fails when a fix has no entry, a fix-titled commit that changes shipped code is neither an entry nor listed in exempt.txt, an entry names a missing test, or the committed ledger is stale. Output: docs/bug-ledger.json and docs/site/docs/bug-ledger.md. D110 and D111 gain a "Pi bug:" line, which marks them as fixed ahead of Pi.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
da907cab0b(2026-10-09) - Tests:
automation/ci/bugledger/bugledger_test.go::TestBuildLedgerFromHistory,automation/ci/bugledger/bugledger_test.go::TestBuildNeedsTheReleaseCommit,automation/ci/bugledger/bugledger_test.go::TestExplicitFixMarkerNamesTheFixCommit,automation/ci/bugledger/bugledger_test.go::TestFixedBulletsReadOnlyTheFixedSectionOfLaterReleases,automation/ci/bugledger/bugledger_test.go::TestImpactAndPlatformComeFromTheWording,automation/ci/bugledger/bugledger_test.go::TestPublicTextDropsPrivateSentencesVersionsAndHashes,automation/ci/bugledger/bugledger_test.go::TestReleaseNotesDraftComesFromTheLedgers,automation/ci/bugledger/bugledger_test.go::TestRootCausePrefersALabelThenCauseWordsThenTheFirstParagraphand 39 more in docs/bug-ledger.json - Release note:
pig docs show divergenceslists D110 and D111, and the README names the pinned Pi release (1.1.0) and its commit. The README gave the pin of two releases ago.
BF-da907ca-4
The Termux page's link to the Quickstart reaches its "Authenticate a provider" section
- Source: PiG's own defect
- Symptom: The Termux page's link to the Quickstart reaches its "Authenticate a provider" section.
- Root cause: Each entry has symptom, root cause, user impact, platform, fix commits, guarding tests and a source class (PiG's own defect, Pi defect fixed ahead of Pi with its divergence number, Pi issue and remove-when, or a Pi fix ported). The check fails when a fix has no entry, a fix-titled commit that changes shipped code is neither an entry nor listed in exempt.txt, an entry names a missing test, or the committed ledger is stale. Output: docs/bug-ledger.json and docs/site/docs/bug-ledger.md. D110 and D111 gain a "Pi bug:" line, which marks them as fixed ahead of Pi.
- User impact: Behavior differed from Pi
- Platform: Android and Termux
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
da907cab0b(2026-10-09) - Tests:
automation/ci/bugledger/bugledger_test.go::TestBuildLedgerFromHistory,automation/ci/bugledger/bugledger_test.go::TestBuildNeedsTheReleaseCommit,automation/ci/bugledger/bugledger_test.go::TestExplicitFixMarkerNamesTheFixCommit,automation/ci/bugledger/bugledger_test.go::TestFixedBulletsReadOnlyTheFixedSectionOfLaterReleases,automation/ci/bugledger/bugledger_test.go::TestImpactAndPlatformComeFromTheWording,automation/ci/bugledger/bugledger_test.go::TestPublicTextDropsPrivateSentencesVersionsAndHashes,automation/ci/bugledger/bugledger_test.go::TestReleaseNotesDraftComesFromTheLedgers,automation/ci/bugledger/bugledger_test.go::TestRootCausePrefersALabelThenCauseWordsThenTheFirstParagraphand 39 more in docs/bug-ledger.json - Release note: The Termux page's link to the Quickstart reaches its "Authenticate a provider" section.
BF-dac3cd5
A session keeps reading its history as it loaded it when another program rewrites, truncates, replaces or…
- Source: PiG's own defect
- Symptom: A read of an entry behind the latest compaction failed or returned only the entry's id, parentId and type.
- Root cause: Owner decision 5 B. Pi reads a session file into memory once (session-manager.ts setSessionFile, loadEntriesFromFile), so a change another program makes to the file afterwards reaches none of its reads. PiG keeps only the active context resident and read the rest back from the session file, so an in-place rewrite, truncation, replacement or removal made the entries behind the latest compaction unreadable (GetEntries marshalled "unexpected end of JSON input").
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),dac3cd5029(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,internal/codingagent/session_context_settings_test.go::TestGetSessionContextSettingsBoundaries,internal/codingagent/session_lazy_test.go::TestLazyEntryOfAChangedRecordIsAnError,internal/codingagent/session_messagefor_cache_test.go::TestMessageForReportsAnUnparseableEntryWithoutParsingItAgain,internal/codingagent/session_snapshot_test.go::TestResumedSessionReadsAsLoadedAfterItsFileChangesand 4 more in docs/bug-ledger.json - Release note: A session keeps reading its history as it loaded it when another program rewrites, truncates, replaces or removes its session file, as Pi does. PiG reads history from a private snapshot of the session file. The snapshot is a file system clone where the file system supports one (Btrfs, XFS, APFS); elsewhere it is a copy that takes the file's size on disk. Before, a read of an entry behind the latest compaction failed or returned only the entry's id, parentId and type.
BF-dae37be
fix: the input loop never waits for the Session lock; the abort-fix leak and two stale tests
- Source: PiG's own defect
- Symptom: fix: the input loop never waits for the Session lock; the abort-fix leak and two stale tests
- Root cause: TestBeginPromptCompactionCheckDoesNotWaitForTheSessionLock (red with Lock). - abortAfterStartProvider (TestNavigateToUserMessageAfterAbortLoadsEditor) never ended its stream and relied on the pre-cancelled request failing in ModelRuntime.start; since an unknown caller-owned provider receives the aborted signal like Pi's compat path, so the fixture reports an aborted message on a cancelled ctx as faux.ts does. ForwardStream drains the provider by design (lazy.ts forwardStream), the provider owns cancellation. - TestBuildModelContextCancelsOAuthRefresh expects the signal's reason (This operation was aborted) the refresh rejects with since. - test_deadline_budget_test.go: delete a stale…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
dae37be433(2026-10-10) - Tests:
coding/cli/model_oauth_refresh_test.go::TestBuildModelContextCancelsOAuthRefresh,coding/session_prompt_compaction_check_test.go::TestBeginPromptCompactionCheckDoesNotWaitForTheSessionLock,coding/session_prompt_compaction_check_test.go::TestBeginPromptCompactionCheckReturnsOnlyADueCompaction,coding/session_prompt_compaction_check_test.go::TestPromptCompactionCheckReplaysAnOverflowedResponse,coding/tree_edit_after_abort_test.go::TestNavigateToUserMessageAfterAbortLoadsEditor
BF-db12780
fix(agent): end the tool flow at custom user messages and hold system messages mid-flow
- Source: PiG's own defect
- Symptom: fix(agent): end the tool flow at custom user messages and hold system messages mid-flow
- Root cause: Pi converts messages to LLM form and then runs transformMessages (ai/src/api/transform-messages.ts), where a branchSummary, compactionSummary, bashExecution or custom message is already a user message and closes the pending tool calls (closePendingToolCalls). Pig normalizes AgentMessages before convertToLLM, so those custom messages did not interrupt the flow and the placeholders landed behind them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
db12780931(2026-10-09) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,coding/session_tree_wire_oracle_test.go::TestNavigateTreeEveryPointSendsValidToolFlow
BF-db1ee65
A lone UTF-16 surrogate in a tool result's details, in a nested tool call's result, or in a code mode call's…
- Source: PiG's own defect
- Symptom: It became three U+FFFD characters (
encoding/jsonhas no lone-surrogate string), so the preview bytes in a stored result differed from Pi's. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
db1ee65be2(2026-10-24) - Tests:
ai/lone_surrogate_json_pi_oracle_test.go::TestMessagesKeepALoneSurrogateAsPiDoes,coding/cli/lone_surrogate_json_lines_pi_oracle_test.go::TestJSONLinesOfAMessageWithALoneSurrogateAreWhatPisJSONStringifyWrites,durable/codemode/lone_surrogate_preview_test.go::TestAPreviewCutInsideASurrogatePairKeepsItsEscapeLikePi,durable/codemode/lone_surrogate_tool_result_test.go::TestAToolResultWithALoneSurrogateKeepsItThroughTheHarnessAndTheEntry,durable/json_lone_surrogate_test.go::TestToJsonValueAndFromJsonValueKeepALoneSurrogate,internal/codingagent/lone_surrogate_session_pi_oracle_test.go::TestSessionLinesWithALoneSurrogateAreWhatPisJSONStringifyWrites - Release note: A lone UTF-16 surrogate in a tool result's details, in a nested tool call's result, or in a code mode call's argument or error preview now stays a lone surrogate through the Durable entry and its stored values, written as the
\ud83descape that Pi'sJSON.stringifywrites, with<,>and&left as they are. Before, it became three U+FFFD characters (encoding/jsonhas no lone-surrogate string), so the preview bytes in a stored result differed from Pi's. The session file, JSON mode and RPC mode already wrote the escape and are unchanged.
BF-db7007d
A Radius gateway model's samplingParamsByThinkingLevel now reaches the pi-messages model and the composed…
- Source: PiG's own defect
- Symptom: A Radius gateway model's
samplingParamsByThinkingLevelnow reaches thepi-messagesmodel and the composed catalog entry (Pi'sgetRadiusModelsFromConfigspreads each gateway model); it was dropped before. - Root cause:
- ai/radius_config.go: RadiusGatewayModel.SamplingParamsByThinkingLevel (radius-config.ts:37-45,61-68 spread each gateway model; types.ts:1133), cloned per level; internal/codingagent/radius_models.go radiusModelEntry carries it to the composed ModelEntry that simple-options.ts:30 reads. Closes the getRadiusModelsFromConfig result row. Tests: ai TestGetRadiusModelsFromConfigCarriesSamplingParamsByThinkingLevel, codingagent TestRadiusCatalogEntryCarriesSamplingParamsByThinkingLevel; three mutants (drop the field in radiusModelEntry, share the per-level map, rename the json tag) fail them. - durable/session documentState helper returns durable.DocumentState (types.ts:842,941-951)…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
339445102a(2026-10-06),77bfe68d1f(2026-10-07),d5d010a275(2026-10-07),db7007dad8(2026-10-07) - Tests:
agent/thinking_level_test.go::TestAgentLoop_RequestsReasoningOnlyForLevelsOtherThanOff,ai/constrained_sampling_upstream_test.go::TestConstrainedSamplingConversionUpstream,ai/openai_responses_foreign_toolcall_test.go::TestResponsesConvertMessages_ForeignCopilotToolCallIDHashedToFcShape,ai/openai_responses_foreign_toolcall_test.go::TestResponsesToolCallID_UnattributedItemIsHashed,ai/openai_responses_shared_oracle_test.go::TestConvertResponsesMessagesMatchesPi,ai/openai_responses_test.go::TestNormalizeResponsesToolCallIDProviderSets,ai/openai_responses_tool_result_output_test.go::TestResponsesConvertMessages_EmptyToolResultUsesPlaceholder,ai/radius_sampling_by_level_test.go::TestGetRadiusModelsFromConfigCarriesSamplingParamsByThinkingLeveland 6 more in docs/bug-ledger.json - Release note: A Radius gateway model's
samplingParamsByThinkingLevelnow reaches thepi-messagesmodel and the composed catalog entry (Pi'sgetRadiusModelsFromConfigspreads each gateway model); it was dropped before.
BF-db9a315
Codemode peak memory is now measured and guarded through the normal tool path in a fresh process with 40…
- Source: PiG's own defect
- Symptom: Codemode peak memory is now measured and guarded through the normal tool path in a fresh process with 40 extension tools exposed.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
db9a31538f(2026-10-14) - Tests:
coding/extension/builtin/codemode/baseline_rss_test.go::TestPeakRSSOfCodemodeScenarios,coding/extension/builtin/codemode/baseline_rss_test.go::TestRSSScenarioChild - Release note: Codemode peak memory is now measured and guarded through the normal tool path in a fresh process with 40 extension tools exposed. Peak RSS is about 62-67 MiB cold (no compilation cache) for a no-op with or without extension tools, about 36 MiB with the compilation cache, about the same for a script passing a 96-record, 18 KB state to a tool, and 41 MiB for the two concurrent scripts the worker limit allows; before the on-demand memory fix a concurrent run reached about 355 MiB. Reported by @kayis-rahman (#211)
BF-dbe1ad3
pig setup now flags a go on PATH older than Go 1.26, the extension SDK minimum, naming the version found…
- Source: PiG's own defect
- Symptom:
pig setupnow flags a go on PATH older than Go 1.26, the extension SDK minimum, naming the version found and the fix, and extension builds use the toolchainpig setup goinstalled instead of that older go (pig additive, D20). - Root cause: pig setup listed a go on PATH older than the extension SDK's Go 1.26 floor without a warning, and after pig setup go PiG still built with that older go, so builds failed under GOTOOLCHAIN=local. pig setup now marks it too old and names the fix, and PiG builds with the toolchain pig setup go installed when the PATH go is below the floor (D20).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
dbe1ad3c2c(2026-10-13) - Tests:
coding/cli/setup_go_floor_test.go::TestSetupStatusFlagsAPathGoBelowTheSDKFloor,internal/toolchain/resolve_test.go::TestResolveGoKeepsAPathGoMeetingTheFloor,internal/toolchain/resolve_test.go::TestResolveGoPrefersManagedGoOverAPathGoBelowTheFloor,internal/toolchain/toolchain_test.go::TestMinGoVersionIsTheSDKGoDirective - Release note:
pig setupnow flags a go on PATH older than Go 1.26, the extension SDK minimum, naming the version found and the fix, and extension builds use the toolchainpig setup goinstalled instead of that older go (pig additive, D20).
BF-dbe25ef
CI: make interface-gaps passes on main again
- Source: PiG's own defect
- Symptom: The interface mapping ledger had not been regenerated for the chord fork replay test that landed with the previous train, so
autobind -checkreported that the ledger is not what the detector derives;make interface-gaps-updaterewrites the two stale rows. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: CI:
make interface-gapspasses on main again. The interface mapping ledger had not been regenerated for the chord fork replay test that landed with the previous train, soautobind -checkreported that the ledger is not what the detector derives;make interface-gaps-updaterewrites the two stale rows.
BF-dbe25ef-10
The bug fix ledger no longer credits a squash commit's tests to a release note that names no fix commit
- Source: PiG's own defect
- Symptom: The bug fix ledger no longer credits a squash commit's tests to a release note that names no fix commit.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: The bug fix ledger no longer credits a squash commit's tests to a release note that names no fix commit. A release note without a
<!-- fix: <sha> -->marker had its commits guessed from the history, and the only commit it found for five notes was the 0.5.0 squash, which grewdocs/bug-ledger.jsonfrom about 2.1 MB to 9.8 MB and pushed the root module past its size budget.make changelog-checknow fails a### Fixedbullet without a marker, a marker or fragment name that names a commitHEADdoes not reach, andmake bug-ledgerrefuses, naming the entry, to attribute the tests of a commit that touches more than 500 files.
BF-dbe25ef-11
Two calls of a Go, Python or Rust extension tool in one assistant message that each open a dialog now behave…
- Source: PiG's own defect
- Symptom: Two calls of a Go, Python or Rust extension tool in one assistant message that each open a dialog now behave as Pi's one thread makes them behave, as Node extensions already did: the dialogs open in call order (a port's second dialog could open first), and when the user answers them back to back the calls respond, and end, in the order of the answers.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: Two calls of a Go, Python or Rust extension tool in one assistant message that each open a dialog now behave as Pi's one thread makes them behave, as Node extensions already did: the dialogs open in call order (a port's second dialog could open first), and when the user answers them back to back the calls respond, and end, in the order of the answers. The SDKs keep these orders for the calls of one parallel batch, which the host now names on each
tool_callrequest (batch); calls of other batches, other sessions' calls and tools that make no host call take no part and are never held. A handler that blocks on something other than a host call before its first host call holds the first dialogs of the later calls of its batch, while a call that awaits a host call such asexecbefore its dialog is overtaken by the later calls' dialogs, as in Pi, and a handler whose dialog was answered holds the later-answered handlers' responses until it returns or makes its next host call (recorded as divergence D187: Pi's single thread lets a call that awaits work the SDK cannot see be overtaken).
BF-dbe25ef-12
pig extension upgrade finds and rewrites every changed API of an extension in one run
- Source: PiG's own defect
- Symptom:
pig extension upgradefinds and rewrites every changed API of an extension in one run. - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note:
pig extension upgradefinds and rewrites every changed API of an extension in one run. Startup offers each Go extension the upgrade would touch, not only those whose first ten compile errors all name an SDK change: alen(ctx.GetAllTools())orpanic(ctx.GetSessionID())(a builtin that takes one value) is now recognised as the getter's change, and a recorded failure with no classification is read again by type checking. AContext.ConfigHomein a function that cannot return the error is rewritten to report the error through the call's ownContext(ctx.Notify) and return, never to discard it; a getter that cannot move before its statement (a&&operand, anelse if, a loop condition, a call after another call) takes a generated helper inpig_upgrade_getters.gothat returns the old getter's zero value on failure.
BF-dbe25ef-13
pig extension upgrade applies the rewrites that have one safe form and lists the rest with file, line and…
- Source: PiG's own defect
- Symptom:
pig extension upgradeapplies the rewrites that have one safe form and lists the rest with file, line and the form to write, instead of restoring every file when one use has no mechanical form. - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note:
pig extension upgradeapplies the rewrites that have one safe form and lists the rest with file, line and the form to write, instead of restoring every file when one use has no mechanical form. It exits 1 and reportspartly upgradedwhen uses are left.--dry-runcompiles the rewritten extension in a temporary directory (the go command's overlay, with the staged SDK) and reportswould upgradeonly when it builds; a rewrite that would add a compile error of its own is not written. The load error of a drifted extension names the changed APIs and the exact command. The stale-copy warning also recognises an edited copy by its Go module path.
BF-dbe25ef-14
The Node extension runtime's ctx.modelRegistry.runtime is now a model runtime that a nested Session can…
- Source: PiG's own defect
- Symptom: The Node extension runtime's
ctx.modelRegistry.runtimeis now a model runtime that a nested Session can share. - Root cause: Not recorded.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: The Node extension runtime's
ctx.modelRegistry.runtimeis now a model runtime that a nested Session can share. An extension that passes it tocreateAgentSession({ modelRuntime }), as the pi-goal-x completion auditor does, failed withthis._modelRuntime.hasConfiguredAuth is not a function. The nested Session now uses the parent's providers and credentials, as in Pi.
BF-dbe25ef-15
Starting a child host could wait out its 30-second ready timeout ("child not ready in time") although the…
- Source: PiG's own defect
- Symptom: Starting a child host could wait out its 30-second ready timeout ("child not ready in time") although the child was up.
- Root cause: Node cannot lose them, because an owner registers in the tick that creates the channel.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: Starting a child host could wait out its 30-second ready timeout ("child not ready in time") although the child was up. The Go port of Node's IPC channel read messages in its own goroutine and dropped any that arrived before its owner had registered a listener. Node cannot lose them, because an owner registers in the tick that creates the channel. A child that opened its channel after the parent's init message had been written could miss the init (so it never answered), and a manager descheduled between spawning the child and registering its control listener could miss the child's ready. The channel now keeps the messages received before its first listener and replays them to it in order, and a forked child instance does the same for its control listeners.
BF-dbe25ef-16
Fixed the chord host's Close waiting forever when a hold that a link was still delivering created an object…
- Source: PiG's own defect
- Symptom: Fixed the chord host's
Closewaiting forever when a hold that a link was still delivering created an object afterClosehad taken its snapshot of the host's objects: that object's resource open kept waiting for an abort that nothing raised. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: Fixed the chord host's
Closewaiting forever when a hold that a link was still delivering created an object afterClosehad taken its snapshot of the host's objects: that object's resource open kept waiting for an abort that nothing raised.Closenow disposes what is left once the links are closed.
BF-dbe25ef-17
pig update --extensions no longer fails when two Git Packages come from one repository
- Source: PiG's own defect
- Symptom:
pig update --extensionsno longer fails when two Git Packages come from one repository. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note:
pig update --extensionsno longer fails when two Git Packages come from one repository. Packages of one repository share a managed checkout, and their updates ran at once: both fetched into oneFETCH_HEADand both rangit reset --hard, which failed withUnable to create '.../.git/index.lock': File existsandgit reset --hard FETCH_HEAD^{commit} failed with code 128. The packages of one checkout now update one at a time, in the order of the settings file, and the last one decides the commit the checkout is left on. Packages from other repositories still update at the same time, and a package that fails does not stop the others.
BF-dbe25ef-18
A Go, Python or Rust extension tool that awaits a host call such as exec before its first dialog no longer…
- Source: PiG's own defect
- Symptom: A Go, Python or Rust extension tool that awaits a host call such as
execbefore its first dialog no longer holds the first dialogs of the later calls of its parallel batch: the SDK passes the call's dialog place at its first host call of any kind, so the later calls' dialogs open while theexecruns and the call's own dialog opens after it, as Pi's single thread does (D187). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: A Go, Python or Rust extension tool that awaits a host call such as
execbefore its first dialog no longer holds the first dialogs of the later calls of its parallel batch: the SDK passes the call's dialog place at its first host call of any kind, so the later calls' dialogs open while theexecruns and the call's own dialog opens after it, as Pi's single thread does (D187). A handler that blocks on something other than a host call before its first host call still holds them.
BF-dbe25ef-19
Two calls of an extension tool in one assistant message now end in the order their tools answer, as Pi ends…
- Source: PiG's own defect
- Symptom: Two calls of an extension tool in one assistant message now end in the order their tools answer, as Pi ends them.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: Two calls of an extension tool in one assistant message now end in the order their tools answer, as Pi ends them. In Pi, two tools that each wait on a dialog (or on timers of equal length) continue in the order their answers arrive, so
tool_execution_endfor the call whose dialog was answered first comes first. PiG woke the goroutines that waited for the two answers in no order, and on most runs the second call ended first, although its dialog was answered second. The host now records the order where the answers are read (atool_callresponse from the extension's process, an RPCextension_ui_responseline) and keeps it where the calls continue: the batch'stool_execution_endevents, and the replies sent to the extension for its dialog calls. Tool-result messages still follow call order.
BF-dbe25ef-2
CI: the Windows test jobs no longer fail the pig extension upgrade notice tests when the temporary…
- Source: PiG's own defect
- Symptom: CI: the Windows test jobs no longer fail the
pig extension upgradenotice tests when the temporary directory holds a~, as a runner's 8.3 short name (RUNNER~1) does. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: CI: the Windows test jobs no longer fail the
pig extension upgradenotice tests when the temporary directory holds a~, as a runner's 8.3 short name (RUNNER~1) does. The notice already quotes such a path for the Windows shell ("C:\Users\RUNNER~1\...") and is unchanged; the tests expected the raw path and now expect the quoted word, as the notice test beside them does.
BF-dbe25ef-20
A compaction, branch summary or bug-report summary request is now sent through the session's own stream…
- Source: PiG's own defect
- Symptom: A session whose stream function was the stock one sent these requests straight to the model's provider, so the host never ran
before_provider_headersfor them. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: A compaction, branch summary or bug-report summary request is now sent through the session's own stream function, as Pi sends it. Before, a session whose stream function was the stock one sent these requests straight to the model's provider, so the host never ran
before_provider_headersfor them./compact, automatic and overflow-recovery compaction,/treesummaries and bug-report summaries now emitbefore_provider_headers, in every mode. They still do not emitbefore_provider_request,after_provider_responseorprovider_stream_event: Pi's summary requests carry none of the Agent's payload, response and stream-event callbacks, and neither do PiG's now (agent requests are unchanged and emit all four).Agent.EffectiveStreamFunctionis new: the stream function a run sends through, with the default stream in place of the nilStreamFunctionreturns for it. Reported by a user.
BF-dbe25ef-21
Fixed pig --mode rpc exiting 143 instead of 0 when stdin ended while a termination signal's shutdown was…
- Source: PiG's own defect
- Symptom: Fixed
pig --mode rpcexiting 143 instead of 0 when stdin ended while a termination signal's shutdown was still running: the first exit of the shutdown now decides the exit status, as Pi's firstprocess.exitdoes. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: Fixed
pig --mode rpcexiting 143 instead of 0 when stdin ended while a termination signal's shutdown was still running: the first exit of the shutdown now decides the exit status, as Pi's firstprocess.exitdoes.
BF-dbe25ef-3
pig extension upgrade: the command a startup notice or the upgrade offer prints now quotes a POSIX path…
- Source: PiG's own defect
- Symptom: The backslash printed bare and the shell read it as an escape, so the command named a different path.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note:
pig extension upgrade: the command a startup notice or the upgrade offer prints now quotes a POSIX path that holds a backslash. Before, the backslash printed bare and the shell read it as an escape, so the command named a different path. A backslash stays plain in a Windows path, where it separates elements.
BF-dbe25ef-4
RPC mode no longer runs ctx.ui.setWidget component factories or emits setWidget requests carrying their…
- Source: PiG's own defect
- Symptom: RPC mode no longer runs
ctx.ui.setWidgetcomponent factories or emitssetWidgetrequests carrying their rendered lines. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: RPC mode no longer runs
ctx.ui.setWidgetcomponent factories or emitssetWidgetrequests carrying their rendered lines. Like Pi'srpc-mode.ts, it forwards string arrays and clears only; print and JSON modes ignore a factory too.
BF-dbe25ef-5
pig piglet verify <binary> --source <name|path> and pig piglet lineage <binary> --source <name|path>…
- Source: PiG's own defect
- Symptom:
pig piglet verify <binary> --source <name|path>andpig piglet lineage <binary> --source <name|path>reportProvenance: matchesfor a Binary built from that unchanged source. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note:
pig piglet verify <binary> --source <name|path>andpig piglet lineage <binary> --source <name|path>reportProvenance: matchesfor a Binary built from that unchanged source. They compared the Piglet digest the Binary signs, which is the digest of its baked Piglet definition, with the extends graph digest, so every check reportedMISMATCH. They now bake the source as the build does. A change to a file the build inlines, such as a system prompt file, is now a Piglet digest mismatch. The JSONprovenanceobject'seffectiveDigestfield is renamedpigletDigest. Binaries built by 0.5.0 and 0.5.1 need no rebuild.
BF-dbe25ef-6
pig piglet verify accepts --workspace <dir>, and pig piglet lineage --workspace now applies to the…
- Source: PiG's own defect
- Symptom:
pig piglet verifyaccepts--workspace <dir>, andpig piglet lineage --workspacenow applies to the provenance check too. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note:
pig piglet verifyaccepts--workspace <dir>, andpig piglet lineage --workspacenow applies to the provenance check too. A Piglet whoseagentEnv.devContaineris aworkspace:path is checked against the workspace its build used instead of failing to resolve from another directory.
BF-dbe25ef-7
A run that an extension's sendMessage with triggerTurn starts now carries the session's system prompt
- Source: PiG's own defect
- Symptom: A run that an extension's
sendMessagewithtriggerTurnstarts now carries the session's system prompt. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: A run that an extension's
sendMessagewithtriggerTurnstarts now carries the session's system prompt. The first run of a session reached the model with no system prompt, as did any later run that a custom message started after the instructions changed. Pi records the run's prompt sections for a custom starting message as for a prompt.
BF-dbe25ef-8
go install github.com/MichaelKinsy/PiG/cmd/pig@main builds again
- Source: PiG's own defect
- Symptom:
go install github.com/MichaelKinsy/PiG/cmd/pig@mainbuilds again. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note:
go install github.com/MichaelKinsy/PiG/cmd/pig@mainbuilds again. The rootgo.modrequiredextensions/sdk v0.5.0, which lacksResult.Drift,FileChange.CreatedandSkipped.Suggestthatpig extension upgradeuses, so building the module outside the workspace failed, andmake module-sizefailed on every pull request. Between releases the rootgo.mod(andpiglets/standardandpiglets/aot) now require the SDK at the pseudo-version of themaincommit that holds the API the root uses; the release replaces it with the tag.make module-publicationaccepts such a pseudo-version, and proves by downloading it into an empty cache, with the committed checksums, that the commit is public.
BF-dbe25ef-9
A tool that an extension activates with setActiveTools now appears in the system prompt's tool list of the…
- Source: PiG's own defect
- Symptom: A tool that an extension activates with
setActiveToolsnow appears in the system prompt's tool list of the next run that atriggerTurnmessage starts. - Root cause: The request already declared the tool, but the prompt left its snippet out, so the model was not told about it.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
dbe25ef5c1(2026-10-21, #261) - Tests:
automation/ci/bugledger/bugledger_test.go::TestAFixTitledSquashWithNoNoteIsRefusedNotAttributed,automation/ci/bugledger/bugledger_test.go::TestTheLedgerRefusesToAttributeTheTestsOfASquash,automation/ci/bugledger/bugledger_test.go::TestTheSquashThresholdIsTheCountOfTouchedFiles,automation/ci/changelogfrag/history_test.go::TestAFragmentNamedAfterAPreRebaseCommitFails,automation/ci/changelogfrag/history_test.go::TestAMarkerNamingAnUnreachableCommitFails,automation/ci/changelogfrag/history_test.go::TestAShallowCloneFailsTheCheckInsteadOfGuessing,automation/ci/changelogfrag/history_test.go::TestEveryCommitOfAMarkerMustBeReachable,automation/ci/changelogfrag/history_test.go::TestEveryFixedBulletNeedsAFixMarkerand 34 more in docs/bug-ledger.json - Release note: A tool that an extension activates with
setActiveToolsnow appears in the system prompt's tool list of the next run that atriggerTurnmessage starts. The request already declared the tool, but the prompt left its snippet out, so the model was not told about it.
BF-dc53412
A pig_strip_bedrock_converse_stream Piglet Binary no longer links the AWS SDK through the Bedrock retry…
- Source: PiG's own defect
- Symptom: A
pig_strip_bedrock_converse_streamPiglet Binary no longer links the AWS SDK through the Bedrock retry delay. - Root cause: bedrock_retry_backoff.go (, the @smithy retry delay) imports the AWS SDK and smithy but had no build tag, so a Piglet Binary built with pig_strip_bedrock_converse_stream linked 27 github.com/aws/ packages again. Its only caller is bedrock.go, which the tag removes.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
dc53412cc2(2026-10-11) - Tests:
coding/cli/strip_providers_binary_test.go::TestStripBedrockConverseStreamBuildOmitsAWSSDK - Release note: A
pig_strip_bedrock_converse_streamPiglet Binary no longer links the AWS SDK through the Bedrock retry delay.
BF-dca5763
A notification, status or message that an extension queues on setImmediate or setTimeout(0) from inside a…
- Source: PiG's own defect
- Symptom: A notification, status or message that an extension queues on
setImmediateorsetTimeout(0)from inside a tool now reaches an RPC client afteragent_settled, as Pi's loop does, instead of beforetool_execution_end. - Root cause: The Node runtime ran them as soon as the tool answered, because it is another process, and its host calls arrived mid-run.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
38901751db(2026-10-21),dca5763ac5(2026-10-21) - Tests:
coding/extension/host/subprocess/callpayload_shape_test.go::TestCallPayloadKeepsUnkeyedLiteralShape - Release note: A notification, status or message that an extension queues on
setImmediateorsetTimeout(0)from inside a tool now reaches an RPC client afteragent_settled, as Pi's loop does, instead of beforetool_execution_end. Pi runs those callbacks when its event loop turns, which a run's chain of microtasks never gives it. The Node runtime ran them as soon as the tool answered, because it is another process, and its host calls arrived mid-run. The host now holds such calls while the RPC run chains microtasks and runs them in arrival order at the next real wait: a model response's I/O, a built-in tool's run, a dialog, a compaction or an auto-retry delay, or the end ofagent_settled. Aprocess.nextTickcallback's notification follows thetool_execution_endof the tool that queued it, as in Pi, and the callbacks of a tool that returns while a sibling tool of its batch still runs reach the client right after itstool_execution_end.
BF-ddd4772
fix(ai): a lone surrogate in a content block stays one code unit, as in JSON.parse
- Source: PiG's own defect
- Symptom: fix(ai): a lone surrogate in a content block stays one code unit, as in JSON.parse
- Root cause: UnmarshalContentBlock and ThinkingContent.UnmarshalJSON decoded with encoding/json, which turns a lone UTF-16 unit into U+FFFD, while the text shorthand of a tool result and the rest of the wire (extensions/sdk/json) keep it as the escape on the wire and WTF-8 in a Go string. A Node or Python tool that returned the block form lost the character; the shorthand kept it. Both forms, and every block a session line carries, now keep it. Also rows for unicode, NUL, U+2028, a 180 KB argument, and Scenario.Unrepresentable for a language limit (Rust strings cannot hold a lone surrogate). Tests: TestUnmarshalContentBlockKeepsALoneSurrogate, TestToolResultKeepsALoneSurrogateInEveryContentForm.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
ddd4772e07(2026-10-10) - Tests:
ai/content_block_test.go::TestUnmarshalContentBlockKeepsALoneSurrogate,coding/extension/host/subprocess/tool_result_member_order_test.go::TestToolResultKeepsALoneSurrogateInEveryContentForm,test/extension-conformance/sdkscenario_test.go::TestSDKScenarios
BF-dde6aa6
Cache warming no longer refreshes at once when a model's promptCache lifetime is far larger than the one-hour…
- Source: PiG's own defect
- Symptom: Cache warming no longer refreshes at once when a model's promptCache lifetime is far larger than the one-hour safety limit.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Cache warming no longer refreshes at once when a model's promptCache lifetime is far larger than the one-hour safety limit. The delay is computed in floating point as upstream does, so the run stops with "one-hour safety limit reached" instead of overflowing to a one-millisecond refresh.
BF-dde6aa6-10
On Windows, path normalization and joining follow Node 24's reserved device name handling: a path such as…
- Source: PiG's own defect
- Symptom: On Windows, path normalization and joining follow Node 24's reserved device name handling: a path such as
CON:or\\?\COM1:keeps its device,path.joinleaves a part naming one unnormalized, and a\\?\nameor\\.\namedevice path no longer gains a trailing backslash. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: On Windows, path normalization and joining follow Node 24's reserved device name handling: a path such as
CON:or\\?\COM1:keeps its device,path.joinleaves a part naming one unnormalized, and a\\?\nameor\\.\namedevice path no longer gains a trailing backslash.
BF-dde6aa6-11
OpenDirReader on a FIFO now fails with not_directory at once instead of waiting for a writer
- Source: PiG's own defect
- Symptom: OpenDirReader on a FIFO now fails with not_directory at once instead of waiting for a writer.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: OpenDirReader on a FIFO now fails with not_directory at once instead of waiting for a writer.
BF-dde6aa6-12
Fixed the queued steering and follow-up lines, the dequeue hint and the startup hint separator to use Pi's…
- Source: PiG's own defect
- Symptom: Fixed the queued steering and follow-up lines, the dequeue hint and the startup hint separator to use Pi's colours: the pending lines use the
dimtheme colour (notmuted) and both close only the foreground colour (ESC[39m) instead of resetting every attribute. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed the queued steering and follow-up lines, the dequeue hint and the startup hint separator to use Pi's colours: the pending lines use the
dimtheme colour (notmuted) and both close only the foreground colour (ESC[39m) instead of resetting every attribute.
BF-dde6aa6-13
A session entry timestamp that is not RFC 3339 but that JavaScript Date reads (a space for the T, an RFC 2822…
- Source: PiG's own defect
- Symptom: A session entry timestamp that is not RFC 3339 but that JavaScript Date reads (a space for the T, an RFC 2822 date) is read for compaction staleness, summary message times and cache-warm entries, and an unreadable compaction timestamp no longer marks every assistant message as older than the compaction.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: A session entry timestamp that is not RFC 3339 but that JavaScript Date reads (a space for the T, an RFC 2822 date) is read for compaction staleness, summary message times and cache-warm entries, and an unreadable compaction timestamp no longer marks every assistant message as older than the compaction.
BF-dde6aa6-14
Fixed the session selector's selected row after a down or page-down key over an empty list: as in Pi the…
- Source: PiG's own defect
- Symptom: Fixed the session selector's selected row after a down or page-down key over an empty list: as in Pi the index becomes -1 and stays out of range after the scope or search changes (no row is highlighted and the position reads
(0/N)) instead of snapping to the first row, and enter, delete and rename no longer act on a row that is not selected. - Root cause: Not recorded.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed the session selector's selected row after a down or page-down key over an empty list: as in Pi the index becomes -1 and stays out of range after the scope or search changes (no row is highlighted and the position reads
(0/N)) instead of snapping to the first row, and enter, delete and rename no longer act on a row that is not selected. Reloading the list in that state no longer panics.
BF-dde6aa6-15
Fixed a failing tool call's result always opening fully expanded: like Pi, an error result collapses to the…
- Source: PiG's own defect
- Symptom: Fixed a failing tool call's result always opening fully expanded: like Pi, an error result collapses to the renderer's preview (a failing bash command shows its last five lines and the expand hint) until the tools are expanded with Ctrl+O or a click.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed a failing tool call's result always opening fully expanded: like Pi, an error result collapses to the renderer's preview (a failing bash command shows its last five lines and the expand hint) until the tools are expanded with Ctrl+O or a click.
BF-dde6aa6-16
Fixed the tree selector's label prompt drawing an input row one cell too wide in a terminal two columns wide…
- Source: PiG's own defect
- Symptom: Fixed the tree selector's label prompt drawing an input row one cell too wide in a terminal two columns wide; the input now gets Pi's
width - 2cells. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed the tree selector's label prompt drawing an input row one cell too wide in a terminal two columns wide; the input now gets Pi's
width - 2cells.
BF-dde6aa6-17
Fixed the card of a tool call for a tool that does not exist (a name the model made up): it now shows the…
- Source: PiG's own defect
- Symptom: Fixed the card of a tool call for a tool that does not exist (a name the model made up): it now shows the tool name, the arguments as indented JSON and the result right below, as Pi does, instead of a compact
key:valuesummary with a blank row before the result. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed the card of a tool call for a tool that does not exist (a name the model made up): it now shows the tool name, the arguments as indented JSON and the result right below, as Pi does, instead of a compact
key:valuesummary with a blank row before the result.
BF-dde6aa6-2
Fixed context edits replacing the content of a system message: as in Pi's projectContextEntry, only user…
- Source: PiG's own defect
- Symptom: Fixed context edits replacing the content of a system message: as in Pi's
projectContextEntry, only user, assistant, tool-result and custom messages take the replacement content, so an edit entry that targets a system message leaves it unchanged in the projected context. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed context edits replacing the content of a system message: as in Pi's
projectContextEntry, only user, assistant, tool-result and custom messages take the replacement content, so an edit entry that targets a system message leaves it unchanged in the projected context.
BF-dde6aa6-3
Fixed the edit tool's diff card for files with CRLF line endings: a diff row that ends in a carriage return…
- Source: PiG's own defect
- Symptom: Fixed the edit tool's diff card for files with CRLF line endings: a diff row that ends in a carriage return is now wrapped like Pi's
Text(which splits on CR as well as LF), so the CR no longer reaches the terminal and the row is followed by Pi's empty wrapped row. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed the edit tool's diff card for files with CRLF line endings: a diff row that ends in a carriage return is now wrapped like Pi's
Text(which splits on CR as well as LF), so the CR no longer reaches the terminal and the row is followed by Pi's empty wrapped row.
BF-dde6aa6-4
Fixed the extension editor dialog to size its text window from the terminal rows (`max(5, floor(rows *…
- Source: PiG's own defect
- Symptom: Fixed the extension editor dialog to size its text window from the terminal rows (
max(5, floor(rows * 0.3))) as Pi does instead of always showing five lines, and its title and the image fallback line to close only the foreground colour (ESC[39m) rather than resetting every attribute. - Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed the extension editor dialog to size its text window from the terminal rows (
max(5, floor(rows * 0.3))) as Pi does instead of always showing five lines, and its title and the image fallback line to close only the foreground colour (ESC[39m) rather than resetting every attribute.
BF-dde6aa6-5
Fixed lists drawn by the submenu and filterable selectors (settings submenus, model and thinking lists)…
- Source: PiG's own defect
- Symptom: Fixed lists drawn by the submenu and filterable selectors (settings submenus, model and thinking lists) showing one label cell in a terminal narrower than five columns; Pi shows the cursor prefix alone.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed lists drawn by the submenu and filterable selectors (settings submenus, model and thinking lists) showing one label cell in a terminal narrower than five columns; Pi shows the cursor prefix alone.
BF-dde6aa6-6
Fixed the footer's context percentage keeping the usage of a reply that a context edit had dropped (a…
- Source: PiG's own defect
- Symptom: Fixed the footer's context percentage keeping the usage of a reply that a context edit had dropped (a response cut off by the token limit): it now refreshes when the edit is appended, as Pi's footer does on its next render.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: Fixed the footer's context percentage keeping the usage of a reply that a context edit had dropped (a response cut off by the token limit): it now refreshes when the edit is appended, as Pi's footer does on its next render.
BF-dde6aa6-7
A table delimiter row on a lazy blockquote line no longer splits the quoted paragraph above it unless it…
- Source: PiG's own defect
- Symptom: A table delimiter row on a lazy blockquote line no longer splits the quoted paragraph above it unless it forms a table with a lazy header of the same cell count.
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: A table delimiter row on a lazy blockquote line no longer splits the quoted paragraph above it unless it forms a table with a lazy header of the same cell count.
BF-dde6aa6-8
A setext underline on a lazy line after another lazy line in a blockquote now renders as Pi does, joined to…
- Source: PiG's own defect
- Symptom: A setext underline on a lazy line after another lazy line in a blockquote now renders as Pi does, joined to the quoted paragraph indented by four spaces.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: A setext underline on a lazy line after another lazy line in a blockquote now renders as Pi does, joined to the quoted paragraph indented by four spaces.
BF-dde6aa6-9
A line of only spaces and tabs that starts with a tab or four spaces, alone or after a blank line or in a…
- Source: PiG's own defect
- Symptom: A line of only spaces and tabs that starts with a tab or four spaces, alone or after a blank line or in a blockquote, is blank in markdown rendering instead of an empty code block, as in Pi.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
dde6aa6a68(2026-10-12) - Tests:
agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,test/docs-drift/docs_drift_test.go::TestDocumentedSlashCommandsExist - Release note: A line of only spaces and tabs that starts with a tab or four spaces, alone or after a blank line or in a blockquote, is blank in markdown rendering instead of an empty code block, as in Pi.
BF-de0aaa7
RPC mode now writes the PIG_PROFILE files when it exits; it ended the process before they were written
- Source: PiG's own defect
- Symptom: RPC mode now writes the
PIG_PROFILEfiles when it exits; it ended the process before they were written. - Root cause: Adds the gates the performance scoreboard needs and that did not exist, all count based (allocations, bytes, GC cycles, RSS/PSS, goroutines, descriptors, children, temp entries, binary size), never time:
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
de0aaa7dec(2026-10-09) - Tests:
ai/perf_gate_norace_test.go::TestProviderStreamPerFrameAllocationGate,ai/perf_gate_norace_test.go::TestStreamBuilderPerDeltaAllocationGate,automation/perf/test_scoreboard.py,coding/extension/host/subprocess/perf_gate_norace_test.go::TestExtensionEventRoundTripAllocationGate,internal/codingagent/compaction/perf_gate_norace_test.go::TestCompactionPreparePerEntryAllocationGate,internal/codingagent/perf_gate_norace_test.go::TestSessionLoadPerLineAllocationGate,internal/codingagent/perf_gate_norace_test.go::TestSessionTurnAllocationGate,internal/perfgate/perfgate_test.go::TestFormatand 18 more in docs/bug-ledger.json - Release note: RPC mode now writes the
PIG_PROFILEfiles when it exits; it ended the process before they were written.
BF-de38ce2
fix(ai,durable): a lone UTF-16 surrogate survives the JSON of a tool result, a nested call result and a…
- Source: PiG's own defect
- Symptom: fix(ai,durable): a lone UTF-16 surrogate survives the JSON of a tool result, a nested call result and a stored value, as in Pi
- Root cause: Fix-Commit:
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
de38ce2324(2026-10-24) - Tests:
ai/lone_surrogate_json_pi_oracle_test.go::TestMessagesKeepALoneSurrogateAsPiDoes,coding/cli/lone_surrogate_json_lines_pi_oracle_test.go::TestJSONLinesOfAMessageWithALoneSurrogateAreWhatPisJSONStringifyWrites,durable/codemode/lone_surrogate_preview_test.go::TestAPreviewCutInsideASurrogatePairKeepsItsEscapeLikePi,durable/codemode/lone_surrogate_tool_result_test.go::TestAToolResultWithALoneSurrogateKeepsItThroughTheHarnessAndTheEntry,durable/json_lone_surrogate_test.go::TestToJsonValueAndFromJsonValueKeepALoneSurrogate,internal/codingagent/lone_surrogate_session_pi_oracle_test.go::TestSessionLinesWithALoneSurrogateAreWhatPisJSONStringifyWrites
BF-de85b0e
An HTTP MCP client opens its server-to-client stream before it sends the next request after…
- Source: PiG's own defect
- Symptom: An HTTP MCP client opens its server-to-client stream before it sends the next request after
notifications/initialized, as Pi does. - Root cause: streamable-http.ts send() calls startGetStream() synchronously, so the server-to-client GET is issued before the client's next request. The Go transport started a goroutine, which under load lost the race to the next POST; the conformance suite's sse-retry scenario then took that first GET for the reconnection and failed client-sse-last-event-id and client-sse-retry-timing in about one run in ten.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
de85b0e873(2026-10-05) - Tests:
mcp/streamable_http_test.go::TestStreamableHTTPTransportWritesTheGETStreamRequestBeforeTheInitializedNotificationReturns - Release note: An HTTP MCP client opens its server-to-client stream before it sends the next request after
notifications/initialized, as Pi does. A server that treats the first GET as a stream reconnection no longer fails about one connection in ten.
BF-de91c76
When an MCP server drops tools or is disabled, its tools are hidden in the order the server listed them
- Source: PiG's own defect
- Symptom: When an MCP server drops tools or is disabled, its tools are hidden in the order the server listed them.
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
de91c76c2f(2026-10-05) - Tests: None recorded.
- Release note: When an MCP server drops tools or is disabled, its tools are hidden in the order the server listed them.
BF-de91c76-2
The error of an MCP stdio server that fails to start ends with the last 2000 UTF-16 code units of its…
- Source: PiG's own defect
- Symptom: The error of an MCP stdio server that fails to start ends with the last 2000 UTF-16 code units of its standard error, as in Pi.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
de91c76c2f(2026-10-05) - Tests: None recorded.
- Release note: The error of an MCP stdio server that fails to start ends with the last 2000 UTF-16 code units of its standard error, as in Pi.
BF-deadeb8
A prompt in a long session no longer costs time in proportion to the session's history: the current branch…
- Source: PiG's own defect
- Symptom: A prompt in a long session no longer costs time in proportion to the session's history: the current branch and its context are cached instead of rebuilt from the whole path several times per prompt (630 ms to 50 ms per prompt at 50,000 entries).
- Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
deadeb8d16(2026-10-10) - Tests:
internal/codingagent/session_store_norace_test.go::TestResumedSessionRetainsContextNotHistory,internal/codingagent/session_store_norace_test.go::TestTurnCostIndependentOfHistoryBehindCompaction,internal/codingagent/session_store_test.go::TestExtensionSessionReadsOfAnIndexedSessionMatchTheWholeLog,internal/codingagent/session_store_test.go::TestLiveSessionReleasesEntriesBehindCompaction,internal/codingagent/session_store_test.go::TestSessionIndexReadsMatchResidentEntries - Release note: A prompt in a long session no longer costs time in proportion to the session's history: the current branch and its context are cached instead of rebuilt from the whole path several times per prompt (630 ms to 50 ms per prompt at 50,000 entries).
BF-deadeb8-2
Extension session reads (getEntry, getLabel, getChildren, getBranch, buildSessionContext and the…
- Source: PiG's own defect
- Symptom: Extension session reads (
getEntry,getLabel,getChildren,getBranch,buildSessionContextand the others) decode only the entries they return instead of the whole session on every call. - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
deadeb8d16(2026-10-10) - Tests:
bench/sessionscale/cmd/sessionscale/gate_test.go::TestResumeMemoryAndPromptCostFollowTheContext,bench/sessionscale/gen_test.go::TestGenerateWritesTheScaledOwnerShape,internal/codingagent/session_store_norace_test.go::TestResumedSessionRetainsContextNotHistory,internal/codingagent/session_store_norace_test.go::TestTurnCostIndependentOfHistoryBehindCompaction,internal/codingagent/session_store_test.go::TestExtensionSessionReadsOfAnIndexedSessionMatchTheWholeLog,internal/codingagent/session_store_test.go::TestLiveSessionReleasesEntriesBehindCompaction,internal/codingagent/session_store_test.go::TestSessionIndexReadsMatchResidentEntries - Release note: Extension session reads (
getEntry,getLabel,getChildren,getBranch,buildSessionContextand the others) decode only the entries they return instead of the whole session on every call.
BF-debe30e
A keybindings.json key named like a property every JavaScript object inherits (constructor, toString…
- Source: PiG's own defect
- Symptom: A
keybindings.jsonkey named like a property every JavaScript object inherits (constructor,toString,__proto__and the rest) is renamed on load the way Pi renames it (to that property's printed text), instead of being kept as written. - Root cause: PiG kept those keys as written. legacyKeybindingName now returns Pi's rename for the twelve inherited names.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
debe30e486(2026-10-12) - Tests:
internal/codingagent/keybindings_file_oracle_test.go::TestKeybindingsFileLoadingMatchesPi - Release note: A
keybindings.jsonkey named like a property every JavaScript object inherits (constructor,toString,__proto__and the rest) is renamed on load the way Pi renames it (to that property's printed text), instead of being kept as written.
BF-dec54eb
fix(tui): Container.Render is safe beside a frame that borrows the window
- Source: PiG's own defect
- Symptom: fix(tui): Container.Render is safe beside a frame that borrows the window
- Root cause: An earlier change made a rebuild write into the array the render two back returned. Container.Render still cloned that array after releasing the lock, and it recycled arrays too. Two regressions followed, both race-free on: - concurrent Render callers raced: a render wrote into the array another caller was still copying; - a frame that borrowed a nested container's lines raced with Render calls on that container from another goroutine, which wrote into the borrowed array.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
dec54ebcc9(2026-10-10) - Tests:
tui/container_render_concurrency_test.go::TestContainerRenderIsSafeForConcurrentCallers,tui/container_render_concurrency_test.go::TestFrameBorrowIsSafeFromConcurrentContainerRender,tui/windowed_render_test.go::TestWindowedFramesEqualWholeFrames
BF-df0b21c
A Go, Rust or Python extension that calls parseFrontmatter or Date.parse while its factory runs, in a…
- Source: PiG's own defect
- Symptom: A Go, Rust or Python extension that calls
parseFrontmatterorDate.parsewhile its factory runs, in a runtime cell shared with other extensions, no longer waits forever when another member of the cell has already connected. - Root cause: In a packed cell the host accepts members in order and serves a connection only after the members before it, so when the ui-peer member had registered first (thread order in the runner is not fixed) the rust-sdk-fixture factory's frontmatter.parse rode ui-peer's connection and waited for an answer the host would not send until the fixture had connected: a deadlock. strace of the stuck runner: the fixture thread sendto(fd of ui-peer's socket, frontmatter.parse) and never returned. Fix: running connections carry the socket they serve; with PIG_EXT_SOCKET set the helper uses the running extension on that socket and otherwise dials it (isolated extensions keep their own connection, an extension…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
07eccbca97(2026-10-11),34f7594aa5(2026-10-11),ca281e10fe(2026-10-11),df0b21c39b(2026-10-12) - Tests:
coding/extension/host/subprocess/runtime_node_model_runtime_test.go::TestNodeLoadTimeCompletionAnswersBeforeRegistration,extensions/sdk/host_helpers_running_test.go::TestHelperConnectionSurvivesTheFirstRunningExtensionStopping,extensions/sdk/host_helpers_running_test.go::TestHelperNamedForASocketDoesNotUseAnotherMembersConnection,internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,internal/codingagent/subprocess_early_model_stream_test.go::TestEarlyModelStreamServesAPICallsWithoutASessionand 5 more in docs/bug-ledger.json - Release note: A Go, Rust or Python extension that calls
parseFrontmatterorDate.parsewhile its factory runs, in a runtime cell shared with other extensions, no longer waits forever when another member of the cell has already connected. The helper now uses the connection of the extension that owns the socket the runner names, and dials that socket when the extension has not connected yet, instead of reusing a sibling's connection that the host serves only later.
BF-df96b6a
Fix in shipped code
- Source: PiG's own defect
- Symptom: Fix in shipped code
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
df96b6a5ac(2026-10-21) - Tests: None recorded.
BF-dfa57b3
An OpenAI Responses request for a conversation with no messages sends "input": [] instead of "input": null
- Source: PiG's own defect
- Symptom: An OpenAI Responses request for a conversation with no messages sends
"input": []instead of"input": null. - Root cause: convertResponsesMessages starts from const messages: ResponseInput = [] (openai-responses-shared.ts:152). convertAnchoredMessages declared a nil slice, so a context with no messages encoded input: null.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
dfa57b3253(2026-10-06) - Tests:
ai/openai_responses_empty_input_test.go::TestConvertAnchoredMessagesEncodesAnEmptyInputAsAnArray - Release note: An OpenAI Responses request for a conversation with no messages sends
"input": []instead of"input": null.
BF-e098463
A Gemini stream chunk with a member of an unexpected type is read as Pi's JavaScript reads it instead of…
- Source: PiG's own defect
- Symptom: A Gemini stream chunk with a member of an unexpected type is read as Pi's JavaScript reads it instead of ending the turn with
google: invalid SSE JSON: a text that is not a string (or null) appends its string form, athoughtorthoughtSignatureof the wrong type is ignored, acandidates, candidate, content orfunctionCallthat is not the expected shape reads as empty,partsthat are a string are skipped and other non-arrays end the turn withcandidate.content.parts is not iterable, a null part ends it with Pi'sCannot read properties of nullerror after the parts before it, a finish reason that is not a string ends it withUnhandled stop reason: <its string form>, and usage that is not an object reads as zeros, and acandidatesTokenCountoftruecounts as 1, as Pi's+makes it. - Root cause: google-generative-ai.ts:236-237 reads
output: (usageMetadata.candidatesTokenCount || 0) + (usageMetadata.thoughtsTokenCount || 0), sotrueadds 1 and the usage stays numeric. PiG's JS read of a mistyped chunk refused a truthy non-number count and ended the turn with the decode error. normalizeGeminiUsage now reads atruecandidates count as 1; every other count is stored as read (a truthy non-number thoughts, total or cached count still ends the turn, because Pi stores it raw). The prompt count is only subtracted from and already counted as its Number(). - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
366969bbae(2026-10-11),5078f73100(2026-10-11),5932542c21(2026-10-11),71e9d13eb9(2026-10-11),82414338e7(2026-10-11),8eb1202528(2026-10-11),b6afa7be1a(2026-10-11),e098463b19(2026-10-11),f1a59f9161(2026-10-11) - Tests:
ai/anthropic_event_member_type_oracle_test.go::TestAnthropicEventMistypedMembersMatchPi,ai/google_chunk_member_type_oracle_test.go::TestGoogleChunkMistypedMembersMatchPi,ai/mistral_chunk_member_type_oracle_test.go::TestMistralChunkMistypedMembersMatchPi,ai/openai_completions_member_type_oracle_test.go::TestOpenAICompletionsMistypedMembersMatchPi,ai/openai_responses_member_type_oracle_test.go::TestOpenAIResponsesMistypedMembersMatchPi - Release note: A Gemini stream chunk with a member of an unexpected type is read as Pi's JavaScript reads it instead of ending the turn with
google: invalid SSE JSON: a text that is not a string (or null) appends its string form, athoughtorthoughtSignatureof the wrong type is ignored, acandidates, candidate, content orfunctionCallthat is not the expected shape reads as empty,partsthat are a string are skipped and other non-arrays end the turn withcandidate.content.parts is not iterable, a null part ends it with Pi'sCannot read properties of nullerror after the parts before it, a finish reason that is not a string ends it withUnhandled stop reason: <its string form>, and usage that is not an object reads as zeros, and acandidatesTokenCountoftruecounts as 1, as Pi's+makes it.
BF-e1764c3
A live /skill: message renders as the collapsed [skill] name block instead of the raw <skill> XML
- Source: PiG's own defect
- Symptom: A live
/skill:message renders as the collapsed[skill] nameblock instead of the raw<skill>XML. - Root cause: Pi's message_start(user) handler calls addMessageToChat, which parses the skill block and shows a SkillInvocationMessageComponent plus the trailing user message (interactive-mode.ts:3501-3505, 3933-3952). PiG did that only when rebuilding the transcript and printed the raw <skill> XML while the turn ran. The user case of the rebuild is now one method shared with the live path. Found by the skills journey.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e1764c3d37(2026-10-09) - Tests:
internal/codingagent/interactive_live_skill_block_test.go::TestLiveUserSkillBlockRendersAsSkillInvocation - Release note: A live
/skill:message renders as the collapsed[skill] nameblock instead of the raw<skill>XML.
BF-e1ad272
The xAI, Kimi Code, Meta, Radius, OpenAI Codex, Sign in with ChatGPT and GitHub Copilot OAuth requests send…
- Source: PiG's own defect
- Symptom: The xAI, Kimi Code, Meta, Radius, OpenAI Codex, Sign in with ChatGPT and GitHub Copilot OAuth requests send their form fields in Pi's order and with Pi's encoding (
*kept,~encoded as%7E) instead of in alphabetical order. - Root cause: xai.ts:73,146-219, kimi-coding.ts:45-236, meta.ts:80,115, radius.ts:162-311 build bodies with new URLSearchParams(fields). url.Values.Encode sorted keys and used QueryEscape. One ordered formFields encoder replaces them; orderedQuery shares it.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
01b94a8249(2026-10-09),e1ad272ec4(2026-10-09) - Tests:
ai/oauth_credential_members_test.go::TestOAuthBuildersWriteCredentialMembersInPiOrder,ai/oauth_form_order_openai_test.go::TestOpenAIAndCopilotOAuthFormBodiesKeepPiFieldOrderAndEncoding,ai/oauth_form_order_test.go::TestOAuthFormBodiesKeepPiFieldOrderAndEncoding - Release note: The xAI, Kimi Code, Meta, Radius, OpenAI Codex, Sign in with ChatGPT and GitHub Copilot OAuth requests send their form fields in Pi's order and with Pi's encoding (
*kept,~encoded as%7E) instead of in alphabetical order.
BF-e24487e
fix(cli): PIG_VERSION_FORMAT=pi prints the pinned Pi package version
- Source: PiG's own defect
- Symptom: fix(cli): PIG_VERSION_FORMAT=pi prints the pinned Pi package version
- Root cause: Pi's --version prints the package version, which differs from the display version when the pin is a pi-mono commit. Use coding.UpstreamPackageVersion so the opt-in always matches pi --version.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
e24487ecef(2026-10-11) - Tests: None recorded.
BF-e25a09f
A tool argument or event payload with a number outside the double range (1e400), at any nesting depth, no…
- Source: PiG's own defect
- Symptom: A tool argument or event payload with a number outside the double range (
1e400), at any nesting depth, no longer fails the call in the Go SDK or ends a Rust extension; it reads as Infinity (Go) ornull(Rust), asJSON.parseandJSON.stringifydo. - Root cause:
- Rust SDK: from_json_str rewrote an out-of-range number only when the first decode met it. A frame nested past serde_json's 128 levels went to the deep decode, whose "number out of range" was returned, so 1e400 inside a 200-deep argument still ended the extension (JSON.parse takes it). The deep result now goes through the same repair. Every cell now registers "Label <name> <value>", which also proves the replacement carries its new label. - The in-process fixture's throw_probe returned "unused" although makeInprocFixture must match testfixture; it now throws the same messages and TestConformance_ThrownToolErrorMessagesAgreeAcrossSDKs runs the in-process Go reference too.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),6229289c22(2026-10-11),78ceab7584(2026-10-11),a4e5b071cb(2026-10-11),e25a09f2ac(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,extensions/sdk-py/tests/test_compaction_estimate.py,extensions/sdk/compaction_estimate_test.go::TestEstimateTokensCountsUTF16UnitsAndJSONStringifyLength,extensions/sdk/compaction_estimate_test.go::TestJSNumberStringMatchesNumberToString,extensions/sdk/json/decode_numbers_test.go::TestUnmarshalReadsANumberOutOfRangeAsInfinityand 8 more in docs/bug-ledger.json - Release note: A tool argument or event payload with a number outside the double range (
1e400), at any nesting depth, no longer fails the call in the Go SDK or ends a Rust extension; it reads as Infinity (Go) ornull(Rust), asJSON.parseandJSON.stringifydo.
BF-e2db931
On Windows, refreshing an MCP server's OAuth token no longer fails with EPERM when another pig process is…
- Source: PiG's own defect
- Symptom: On Windows, refreshing an MCP server's OAuth token no longer fails with
EPERMwhen anotherpigprocess is still deleting the refresh lock. - Root cause: The prune loop checks for cancellation only before each entry. A busy failure that arrived after the cancellation counted as skipped, and the last entry's classification could be cut short with nothing to remove, so the collection returned success and the automatic collector wrote its marker for a run that did not finish. The collection now checks for cancellation before it reports success.
- User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: 0.5.0
- Fix commits:
ba9c37b247(2026-10-06, #171),e2db9318fa(2026-10-06, #173) - Tests:
coding/extension/exec_tree_test.go::TestExecCommandKeepsOutputWrittenAfterTheLeaderExits,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleBusyEntriesAreSkippedNotErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledBeforeStartTouchesNothing,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleCancelledDuringTheLastEntryReportsTheCancellation,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleEntryLeasedAfterClassificationIsSkippedNotAnError,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleOtherErrorsStayErrors,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestCacheLifecycleStopsWhenItsContextIsCancelled,coding/extension/host/runtimecell/cache_lifecycle_busy_test.go::TestIsBusyErrorLooksThroughWrappedJoinsand 4 more in docs/bug-ledger.json - Release note: On Windows, refreshing an MCP server's OAuth token no longer fails with
EPERMwhen anotherpigprocess is still deleting the refresh lock. Lock acquisition that waits for a lock now retries every error, as Pi's lock does, and reports the most frequent error when it gives up.
BF-e32c60c
A Piglet or Package installed under a symlinked agent directory keeps the origin integrity recorded in its…
- Source: PiG's own defect
- Symptom: A Piglet or Package installed under a symlinked agent directory keeps the origin integrity recorded in its npm lockfile.
- Root cause:
- env: the remote watcher reports paths as given (remote-env.ts #resolve); drop the symlink resolution from the test expectation. - cmd/pig: the warning-only diagnostics name the package as listed (package-manager.ts packageRoot). - coding/piglet: resolve npm lockfile entries whose keys npm writes relative to a symlinked install root, so a symlinked agent directory no longer loses the origin integrity. - interactive thinking harness: drain the PTY until pig exits. - subprocess and experimental tests: use short socket directories. - experimental: wait for a removed socket by polling under a deadline, since fsnotify v1.5.4 reports no removal on macOS. - CI: run cmd/pig, env, the extension…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e32c60c500(2026-10-06) - Tests:
coding/extension/host/subprocess/node_frame_buffer_test.go::TestNodeProviderSocketFrameCopyIsLinear,coding/piglet/origin_integrity_test.go::TestReadNPMOriginIntegrityResolvesLockfileKeys,env/watch_test.go::TestRemoteWatcherOpensAgainAfterALostConnectionAndReportsOverflow,test/ci-images/macos_shards_test.go::TestMacOSShardsTestTheMacOSSensitivePackages - Release note: A Piglet or Package installed under a symlinked agent directory keeps the origin integrity recorded in its npm lockfile. Lockfile entries that npm writes relative to a symlinked install root were not found.
BF-e3f39b7
Automatic retry now triggers for the gateway error "Unable to forward upstream stream"
- Source: PiG's own defect
- Symptom: Automatic retry now triggers for the gateway error "Unable to forward upstream stream".
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e3f39b7bee(2026-10-14, #10920) - Tests:
ai/assistant_retry_test.go::TestIsRetryableAssistantErrorClassification - Release note: Automatic retry now triggers for the gateway error "Unable to forward upstream stream". Carried forward from Pi 7efebff1 (earendil-works/pi#10920), past the 1.2.0+3daa2028 pin.
BF-e42ae82
SettingsManager structured getters (settings-manager.ts getBranchSummarySettings, getRetrySettings…
- Source: PiG's own defect
- Symptom:
SettingsManagerstructured getters (settings-manager.ts getBranchSummarySettings, getRetrySettings, getProviderRetrySettings, getCacheWarmingMode, getQuietStartup, getDefaultProjectTrust, getDoubleEscapeAction, getTreeFilterMode, getOutputPad, getMermaidRenderingMode, getImageWidthCells, getTuiMode, the fullscreen getters, getEnabledModels, getNpmCommand, getTrackingId and others): new live-Pi oracleTestSettingsManagerStructuredGettersMatchPi(2,000 layered global/project file pairs). - Root cause: Real fixes: mergeSettings ignored project enableAnalytics and trackingId; terminal.imageWidthCells of 0 or negative read as 60 instead of Pi's max(1, floor).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e42ae82264(2026-10-10) - Tests:
internal/codingagent/settings_structured_getters_oracle_test.go::TestSettingsManagerStructuredGettersMatchPi,internal/codingagent/settings_structured_getters_oracle_test.go::TestSettingsWriteKeepsAnAuthoredNonPositiveImageWidthCells - Release note:
SettingsManagerstructured getters (settings-manager.ts getBranchSummarySettings, getRetrySettings, getProviderRetrySettings, getCacheWarmingMode, getQuietStartup, getDefaultProjectTrust, getDoubleEscapeAction, getTreeFilterMode, getOutputPad, getMermaidRenderingMode, getImageWidthCells, getTuiMode, the fullscreen getters, getEnabledModels, getNpmCommand, getTrackingId and others): new live-Pi oracleTestSettingsManagerStructuredGettersMatchPi(2,000 layered global/project file pairs). Two real fixes: (1) a project settings file'senableAnalyticsandtrackingIdwere ignored by the merge (deepMergeSettings lets every project key override), so a projectenableAnalytics: trueread as false; (2)terminal.imageWidthCellsof 0 or a negative number read as the 60 default, where Pi's getImageWidthCells readsMath.max(1, Math.floor(width))= 1; an authored value is now tracked (imageWidthCellsSet), merged from the project file and written back. RegressionTestSettingsWriteKeepsAnAuthoredNonPositiveImageWidthCells. 15 mutations red. Not compared: environment-reading getters, wrong-typed values and fractional numbers (Settings is typed), empty strings.
BF-e48a02f
fix(tui): filterable list drops the label when no cell remains (Pi select-list)
- Source: PiG's own defect
- Symptom: fix(tui): filterable list drops the label when no cell remains (Pi select-list)
- Root cause: select-list.ts renderItem computes maxWidth = width - 2 - 2 without a floor and truncateToWidth returns "" for maxWidth <= 0; Pig's FilterableList floored it at 1, drawing one label cell at widths 3-4. TestFilterableListShowsNoLabelWhenNoCellRemains is red without the fix.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
e48a02f7ec(2026-10-11) - Tests:
internal/codingagent/settings_selector_oracle_test.go::TestSettingsSelectorMatchesPi,tui/select_filterable_test.go::TestFilterableListShowsNoLabelWhenNoCellRemains
BF-e51bb37
OpenAI Responses streams follow Pi's processResponsesStream on malformed and edge events, checked against…
- Source: PiG's own defect
- Symptom: OpenAI Responses streams follow Pi's
processResponsesStreamon malformed and edge events, checked against the pinned Pi in a new oracle test: a failed response records only its status (no usage or response id),end_turnis recorded only for Codex, anerrorevent without a code or message readsError Code undefined: undefined, aresponse.createdor terminal event without a response reports Pi's TypeError text, and a reasoning signature backfilled withencrypted_contentkeeps its original key order. - Root cause: It exposed five differences, fixed at the source: a failed response leaked usage and response id (Pi records only the status); end_turn was recorded outside Codex; an error event without code or message printed empty text where Pi prints undefined; a created or terminal event without a response gave a Go message where Pi gives its TypeError text; the encrypted_content backfill re-sorted the signature's keys where Pi keeps insertion order. Custom tool input accumulation gets a direct test. ~65 mutants run; evidence recorded. ai unproven rows 153 -> 151.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e8cc072521(2026-10-08),e51bb373be(2026-10-10) - Tests:
ai/auth_store_test.go::TestInMemoryCredentialStoreListRejectsACancelledContext,ai/cloudflare_auth_upstream_test.go::TestCloudflareWorkersAIAuthMergesCredentialAndEnvironmentPerField,ai/convert_completions_proof_test.go::TestConvertCompletionsMessagesAddsEmptyReasoningContentOnlyWhenNoneWasCopied,ai/convert_completions_proof_test.go::TestConvertCompletionsMessagesJoinsAssistantThinkingByTheReasoningFieldRules,ai/convert_completions_proof_test.go::TestConvertCompletionsMessagesKeepsTheFirstReasoningDetailsAndFallsBackToLegacyOnes,ai/convert_completions_proof_test.go::TestConvertCompletionsMessagesNeedsAnAssistantBridgeAfterToolResultsOnlyWhenTheLastKeptMessageWasOne,ai/convert_completions_proof_test.go::TestConvertCompletionsMessagesSanitizesStringUserTextAndToolResultText,ai/convert_completions_proof_test.go::TestConvertCompletionsMessagesSendsASanitizedMidConversationInstructionInTheInstructionRoleand 12 more in docs/bug-ledger.json - Release note: OpenAI Responses streams follow Pi's
processResponsesStreamon malformed and edge events, checked against the pinned Pi in a new oracle test: a failed response records only its status (no usage or response id),end_turnis recorded only for Codex, anerrorevent without a code or message readsError Code undefined: undefined, aresponse.createdor terminal event without a response reports Pi's TypeError text, and a reasoning signature backfilled withencrypted_contentkeeps its original key order.
BF-e54c2a7
openai-decisions writes the image input's user message, text part and image parts with their members in…
- Source: PiG's own defect
- Symptom:
openai-decisionswrites the image input's user message, text part and image parts with their members in Pi's order (rolebeforecontent,typebefore the value), not in Go map order. - Root cause: 700 random states, questions, image counts, prices with prompt-length tiers, base URLs and responses (well-formed, malformed, refused, missing and extra answers, usage objects, HTTP errors) run through the pinned classifier and ClassifyOpenAIDecisions against one scripted server: request, stop reason, error message, answers and priced usage agree. The one difference found: the user message of an image request was a Go map and went out role-last; it is a struct in Pi's member order now (openai-decisions.ts:101-113).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
91f99469d5(2026-10-11),e1abc6c615(2026-10-11),e54c2a7fa2(2026-10-11) - Tests:
ai/llama_cpp_classify_oracle_test.go::TestLlamaCppClassifyMatchesPi,ai/openai_decisions_oracle_test.go::TestOpenAIDecisionsMatchesPi,ai/openrouter_images_edge_oracle_test.go::TestOpenRouterImagesEdgeResponsesMatchPi - Release note:
openai-decisionswrites the image input's user message, text part and image parts with their members in Pi's order (rolebeforecontent,typebefore the value), not in Go map order.
BF-e55895a
Durable: opening a store that pi-durable 1.3.0 wrote no longer leaves you unable to go back to it
- Source: PiG's own defect
- Symptom: Durable: opening a store that pi-durable 1.3.0 wrote no longer leaves you unable to go back to it.
- Root cause: PiG's Durable writes schema version 2, which pi-durable 1.3.0 refuses, and opening a version 1 store by file path upgraded it in place, so a user could not go back to the released Pi. The fix copies such a store to <path>.schema-<N>.bak (VACUUM INTO) before the upgrade; an earlier copy is never replaced, and a failed copy fails the open.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
c6cb4f6c37(2026-10-20),e55895a96d(2026-10-21) - Tests:
durable/core/driver/schema_backup_core_test.go::TestOpenHarnessCopiesAnOlderSchemaBeforeUpgrading,durable/storage/sqlite/node/schema_backup_test.go::TestOpenNodeSqliteStorageCopiesAnOlderSchemaBeforeUpgrading,durable/storage/sqlite/node/schema_backup_test.go::TestOpenNodeSqliteStorageDoesNotCopyANewOrCurrentStore - Release note: Durable: opening a store that pi-durable 1.3.0 wrote no longer leaves you unable to go back to it. PiG's Durable writes schema version 2, which no Pi release reads yet, and it upgraded the store in place.
driver.OpenHarnessandnode.OpenNodeSqliteStoragenow copy a store at an older schema version to<path>.schema-1.bakbefore upgrading it; pi-durable 1.3.0 opens that copy. An earlier copy is never replaced, and if the copy cannot be written the open fails and the store is left as it was. The copy is one extra file the size of the store, written once per older store (D188, #245; the commit that made the change calls it D186, its number before the renumber).
BF-e55cdc2
The Codex catalog entry for GPT-6.1 Sol reports the 500K context window the Codex models endpoint returns…
- Source: PiG's own defect
- Symptom: The Codex catalog entry for GPT-6.1 Sol reports the 500K context window the Codex models endpoint returns; other Codex models keep 272K (carried Pi fix, Pi 5712b134, #10840).
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e55cdc239a(2026-10-14) - Tests:
ai/supports_xhigh_upstream_test.go::TestSupportsXHighUpstream - Release note: The Codex catalog entry for GPT-6.1 Sol reports the 500K context window the Codex models endpoint returns; other Codex models keep 272K (carried Pi fix, Pi 5712b134, #10840).
BF-e59f3f6
Fixed tool-call arguments losing a field and every field after it when the model wrote an invalid \u escape…
- Source: PiG's own defect
- Symptom: Fixed tool-call arguments losing a field and every field after it when the model wrote an invalid
\uescape such as\usepackage; the escape is now kept as literal text like other invalid escapes (Pi 4b994d5e, #10868). - Root cause: repairJSON treated \u as a valid escape even when four hex digits did not follow, so \usepackage left the JSON unparseable and the partial parser dropped the field and every field after it. A \u without four hex digits is now an invalid escape whose backslash is doubled, like \q (Pi utils/json-parse.ts, #10868). Ports packages/ai/test/json-parse.test.ts; the two tests that pinned the old behaviour now pin the new one.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e59f3f62ed(2026-10-13) - Tests:
ai/json_parse_upstream_test.go::TestParseStreamingJsonDecodesValidUnicodeEscapes,ai/json_parse_upstream_test.go::TestParseStreamingJsonKeepsInvalidUnicodeEscapesAsLiteralText,ai/json_parse_upstream_test.go::TestParseStreamingJsonOmitsTruncatedUnicodeEscape,ai/sse_json_parity_test.go::TestParseJSONWithRepairKeepsInvalidUnicodeEscapesLiteral - Release note: Fixed tool-call arguments losing a field and every field after it when the model wrote an invalid
\uescape such as\usepackage; the escape is now kept as literal text like other invalid escapes (Pi 4b994d5e, #10868).
BF-e5a345e
An Anthropic request holds a mid-conversation system update until an assistant message that is actually sent…
- Source: A fix Pi shipped that PiG ported
- Symptom: An Anthropic request holds a mid-conversation system update until an assistant message that is actually sent, so an assistant message that converts to no blocks no longer moves the update before a user message.
- Root cause: Ports Pi (#11091). A mid-conversation system message is held until an assistant message that is actually sent (or the end of the transcript), so an assistant message that converts to no blocks no longer lets the update land before a user message.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.3
- Fix commits:
e5a345eeba(2026-10-22) - Tests:
ai/transcript_tool_changes_test.go::TestTranscriptToolChangesAnthropicHoldsUpdatePastAssistantThatConvertsToNoBlocks - Release note: An Anthropic request holds a mid-conversation system update until an assistant message that is actually sent, so an assistant message that converts to no blocks no longer moves the update before a user message.
BF-e5aae63
The editor now enters bash mode (bash border color, Escape clears it) when the text starts with ! after any…
- Source: PiG's own defect
- Symptom: The editor now enters bash mode (bash border color, Escape clears it) when the text starts with
!after any JavaScript whitespace, including blank lines and non-breaking spaces, as in Pi. - Root cause: setupKeyHandlers' onChange sets isBashMode from the whole text after JavaScript trimStart. PiG trimmed only spaces and tabs of the first line.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e5aae639c1(2026-10-13) - Tests:
internal/codingagent/tool_codemode_timer_test.go::TestCodemodeRowShowsElapsedWhileRunningAndTookOnceFinished,tui/bash_execution_test.go::TestEditorBashModeTrimsTheWholeTextAsJavaScript,tui/terminal_image_test.go::TestHerdrWithGraphicsSignalRendersKittyImage - Release note: The editor now enters bash mode (bash border color, Escape clears it) when the text starts with
!after any JavaScript whitespace, including blank lines and non-breaking spaces, as in Pi. PiG looked only at spaces and tabs on the first line.
BF-e5f9fdd
The provider retry transport no longer replaces Body on the caller's request when it retries
- Source: PiG's own defect
- Symptom: The provider retry transport no longer replaces
Bodyon the caller's request when it retries. - Root cause: A RoundTripper must not modify its request, and the base transport can still be reading the previous attempt's body after an early 503 with Connection: close. Regression: TestRetryTransport_EarlyResponseDoesNotRaceRequestBody.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e5f9fdd166(2026-10-06) - Tests:
ai/provider_retry_test.go::TestRetryTransport_EarlyResponseDoesNotRaceRequestBody,coding/mcpext/connection_test.go::TestMCPConnectionsClosesAConnectionThatIsStillInitializingBeforeCloseReturns,coding/mcpext/connection_test.go::TestMCPConnectionsStopsWaitingToRetryAConnectionWhenClosed - Release note: The provider retry transport no longer replaces
Bodyon the caller's request when it retries. Each retry sends its own request clone with a fresh body, so a server that answers 503 withConnection: closebefore reading a large request body no longer races the HTTP transport's body writer.
BF-e63c61c
fix(interactive): pump the owner loop while model selection awaits extensions (#208) (#209)
- Source: PiG's own defect
- Symptom: fix(interactive): pump the owner loop while model selection awaits extensions (#208) (#209)
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
e63c61c338(2026-10-12, #208) - Tests:
internal/codingagent/interactive_model_tools_deadlock_test.go::TestInteractiveModelSelectionPumpsToolRegistration
BF-e64d2cf
fix(tui): a tool error result collapses like any other (Pi updateResult)
- Source: PiG's own defect
- Symptom: fix(tui): a tool error result collapses like any other (Pi updateResult)
- Root cause: tool-execution.ts updateResult never changes the expansion; bash/read/write renderers show a collapsed preview for an error as for a success. Found by e2e runs of scripted replies through real Pi and Pig (TUI panes and session files compared). TestErrorResultStaysCollapsedUntilExpanded is red without the change. NOTE: TestRefreshModelRuntimeHonoursNetworkForceErrorsAndRegistrationCover fails ('TempDir RemoveAll cleanup: directory not empty') at -count>1 and in full-package runs on the integrate base, unrelated.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
e64d2cfe13(2026-10-11) - Tests:
internal/codingagent/model_registry_availability_refresh_gaps_test.go::TestRefreshModelRuntimeHonoursNetworkForceErrorsAndRegistrationCover,tui/tool_execution_test.go::TestErrorResultStaysCollapsedUntilExpanded
BF-e69d018
Closing an env connection, or losing it, now ends its daemon process with SIGTERM, as in Pi, instead of…
- Source: PiG's own defect
- Symptom: Closing an
envconnection, or losing it, now ends its daemon process with SIGTERM, as in Pi, instead of SIGKILL, so a daemon reached throughsshcan close its remote side. - Root cause: Pi's Connection #teardown (connection.ts:307-320) ends the daemon's stdin and calls child.kill(), Node's SIGTERM. PiG's teardown called Process.Kill (SIGKILL), so an ssh child could not close its remote side. It now uses terminateProcess, the helper runSsh's timeout uses (a plain kill on Windows, as Node does).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
50233fd0d4(2026-10-10),e69d0181ec(2026-10-10) - Tests:
env/connection_teardown_signal_test.go::TestConnectionTeardownEndsTheDaemonWithSIGTERM,env/ssh_known_hosts_semantics_test.go::TestConcurrentHostKeyChangesToOneFileAreAppliedOneAtATime,env/ssh_timeout_test.go::TestRunSshTimeoutTerminatesTheChildAndFailsWithoutWaitingForItsOutputToClose - Release note: Closing an
envconnection, or losing it, now ends its daemon process with SIGTERM, as in Pi, instead of SIGKILL, so a daemon reached throughsshcan close its remote side.
BF-e73e536
Codemode scripts no longer fail immediately when both codemode workers are busy, for example with parallel…
- Source: A fix Pi shipped that PiG ported
- Symptom: Codemode scripts no longer fail immediately when both codemode workers are busy, for example with parallel codemode calls or in-process subagents.
- Root cause: Ports Pi (#11184): scripts wait up to 60 s in arrival order for a free VM, PI_CODEMODE_MAX_WORKERS raises the limit of two, aborts and deadlines count the wait, a launch resuming after its result settled starts no worker, and the reservation is released exactly once.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
e73e536f2c(2026-10-20) - Tests:
codemode/worker_settled_launch_test.go::TestLaunchResumingAfterTheResultSettledTakesNoWorker,coding/extension/builtin/codemode/worker_admission_test.go::TestCodemodeWorkerAdmissionBoundsSurvivingWorkers,coding/extension/builtin/codemode/worker_admission_test.go::TestCodemodeWorkerAdmissionDropsAbortedAndTimedOutWaiters,coding/extension/builtin/codemode/worker_admission_test.go::TestCodemodeWorkerAdmissionIgnoresAnInvalidPICodemodeMaxWorkers,coding/extension/builtin/codemode/worker_admission_test.go::TestCodemodeWorkerAdmissionQueuesABurstInArrivalOrder,coding/extension/builtin/codemode/worker_admission_test.go::TestCodemodeWorkerAdmissionReadsTheLimitFromPICodemodeMaxWorkers,coding/extension/builtin/codemode/worker_admission_test.go::TestCodemodeWorkerAdmissionRefusesAnAbortedCallerAtOnce,coding/extension/builtin/codemode/worker_admission_test.go::TestCodemodeWorkerSlotIsReturnedAfterSuccessAndTimeoutand 2 more in docs/bug-ledger.json - Release note: Codemode scripts no longer fail immediately when both codemode workers are busy, for example with parallel codemode calls or in-process subagents. A script waits up to 60 seconds, in arrival order, for a free worker; the wait counts toward its
timeout_ms(Pi 885a564b, #11184).
BF-e75a8b9
The external editor (ctrl+g) now edits prompt.md inside a private pi-editor-* directory under the…
- Source: PiG's own defect
- Symptom: The external editor (
ctrl+g) now editsprompt.mdinside a privatepi-editor-*directory under the system temp directory and removes the directory afterwards, as Pi does. - Root cause:
- external-editor.ts (editInExternalEditor, external-editor.ts:15-45) writes prompt.md inside mkdtempSync(join(tmpdir(), "pi-editor-")) and removes the directory; OpenExternalEditor wrote pig-editor-*.md straight into the shared temp directory. TestExternalEditorUpstream ports the three cases of external-editor.test.ts:36,52,58 (red on the old code: wrong directory, file name, entries, leftover directory); the test-mapping row now names it instead of "direct name match". They carry
// upstream:references to session-selector.ts setStatusMessage (:837,858,862,1003) and formatSessionDate (:43). The cell names its load, status and delete helpers in prose again. - pi_wsl_test.go duplicated…
- external-editor.ts (editInExternalEditor, external-editor.ts:15-45) writes prompt.md inside mkdtempSync(join(tmpdir(), "pi-editor-")) and removes the directory; OpenExternalEditor wrote pig-editor-*.md straight into the shared temp directory. TestExternalEditorUpstream ports the three cases of external-editor.test.ts:36,52,58 (red on the old code: wrong directory, file name, entries, leftover directory); the test-mapping row now names it instead of "direct name match". They carry
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e75a8b9cbd(2026-10-09) - Tests:
internal/codingagent/external_editor_test.go::TestEditorHelperProcess,internal/codingagent/external_editor_test.go::TestExternalEditorUpstream,internal/codingagent/wsl_test.go::TestIsWSL - Release note: The external editor (
ctrl+g) now editsprompt.mdinside a privatepi-editor-*directory under the system temp directory and removes the directory afterwards, as Pi does. It previously wrote apig-editor-*.mdfile directly in the shared temp directory.
BF-e79c70e
fix(ai): Codex WebSocket, OpenRouter images, pi-messages and replaced Anthropic bodies write JSON.stringify…
- Source: PiG's own defect
- Symptom: fix(ai): Codex WebSocket, OpenRouter images, pi-messages and replaced Anthropic bodies write JSON.stringify text
- Root cause: An earlier change moved provider request bodies to marshalRequestBody but left four encoders on encoding/json.Marshal, which escapes '<', '>', '&', U+2028 and U+2029 where Pi's JSON.stringify writes them as they are:
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
e79c70ea9e(2026-10-10) - Tests:
ai/request_body_escaping_internal_test.go::TestAnthropicReplacedPayloadWritesCharactersAsJSONStringifyDoes,ai/request_body_escaping_internal_test.go::TestCodexWebSocketRequestWritesCharactersAsJSONStringifyDoes,ai/request_body_escaping_internal_test.go::TestOpenRouterImagesRequestWritesCharactersAsJSONStringifyDoes,ai/request_body_escaping_internal_test.go::TestPiMessagesRequestWritesCharactersAsJSONStringifyDoes,ai/request_body_escaping_test.go::TestProviderRequestBodiesWriteCharactersAsJSONStringifyDoes
BF-e8489d4
A key hint for an app action the user unbound shows no key, as in Pi, instead of the action's default key
- Source: PiG's own defect
- Symptom: A key hint for an app action the user unbound shows no key, as in Pi, instead of the action's default key.
- Root cause: Pi keyHint formats getKeybindings().getKeys(action); an action bound to [] gives "" (keybinding-hints.ts formatKeys). AppKeyText fell back to the default key whenever the installed resolver returned "", so the extension editor hint row still showed ctrl+g for an unbound app.editor.external. The fallback now applies only when no resolver is installed (component tests outside the interactive mode).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e8489d4003(2026-10-11),fac470319e(2026-10-11) - Tests:
internal/codingagent/extension_editor_test.go::TestExtensionEditorHandleInputMatchesPi - Release note: A key hint for an app action the user unbound shows no key, as in Pi, instead of the action's default key. For example, the extension editor dialog showed
ctrl+g external editorafterapp.editor.externalwas bound to[]; it now showsexternal editor(tui/keybinding_hints.goAppKeyText, keybinding-hints.ts keyText; probe inTestExtensionEditorHandleInputMatchesPi, red before).
BF-e90ac19
Very long streamed answers no longer slow down with their length: each streamed frame renders only the last…
- Source: PiG's own defect
- Symptom: Very long streamed answers no longer slow down with their length: each streamed frame renders only the last blocks of the message that can still change instead of lexing, wrapping and laying out all of it, so a 60,000-character answer costs about 5% of the CPU it did, with the same output.
- Root cause: Tests: - TestStreamedMarkdownWithAForeignThemeCallsItsStylesAgain and TestStreamedMarkdownWithAForeignTextColorCallsItAgain use style functions that read a changing generation. - TestStreamedMarkdownFollowsTheDefaultStyle changes one input per step. - TestStreamedMarkdownEqualsAFreshRender now streams with the default theme. Its reference reuses nothing, because one of its styles is wrapped, so a key without the lexer state fails it. - Mutants red: no purity check, no style flags, no default italic, a foreign Color allowed, no context comparison, no lexer state in the key.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
06f40b8ca1(2026-10-10),136e3703be(2026-10-10),79eadcf623(2026-10-10),8693d2dfa9(2026-10-10),9f76da76d0(2026-10-10),ab5be97bbe(2026-10-10),c4fef6c21a(2026-10-10),e892003a81(2026-10-10),e90ac19595(2026-10-10) - Tests:
internal/codingagent/complexity_scaling_mermaid_norace_test.go::TestScalingMermaidTransformOfAStreamedMessage,internal/codingagent/mermaid_word_test.go::TestMermaidTransformSkipsOnlyMessagesWithoutTheWord,internal/testscaling/testscaling_test.go::TestBytesBoundSeparatesTheByteSlope,tui/complexity_scaling_norace_test.go::TestScalingBashOutputFrame,tui/complexity_scaling_norace_test.go::TestScalingMainScreenFrames,tui/complexity_scaling_norace_test.go::TestScalingStreamingMessage,tui/container_render_cache_test.go::TestMainScreenCursorExtractionDoesNotMutateBorrowedContainerLines,tui/highlight_cache_bound_test.go::TestHighlightMemoKeepsOnlyTheNewestPrefixOfAStreamedBlockand 11 more in docs/bug-ledger.json - Release note: Very long streamed answers no longer slow down with their length: each streamed frame renders only the last blocks of the message that can still change instead of lexing, wrapping and laying out all of it, so a 60,000-character answer costs about 5% of the CPU it did, with the same output.
BF-e9332c8
fix(codingagent): session search re: reads patterns as V8 does
- Source: PiG's own defect
- Symptom: fix(codingagent): session search re: reads patterns as V8 does
- Root cause: Random JavaScript patterns against Pi's parseSearchQuery/filterAndSortSessions: 441 of 3000 differed (quantified assertions, [a-\d], astral literals, \b \w \s, case folding of U+0130/U+212A, (?s:) groups, error text). translateJSRegExp parses the Annex B grammar with V8's messages and emits regexp2 source over UTF-16 units; annexBRegexpSource is gone.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
e9332c8ccf(2026-10-10) - Tests:
internal/codingagent/session_search_regex_random_oracle_test.go::TestSessionSearchRegexMatchesPi
BF-e9392fd
A Google Generative AI or Vertex request with no contents (a history of system messages only, or one user…
- Source: PiG's own defect
- Symptom: A Google Generative AI or Vertex request with no
contents(a history of system messages only, or one user message without content) now fails withcontents are requiredbefore it is sent, as Pi's@google/genaiSDK does, instead of reaching the API and failing with HTTP 400. - Root cause: prepareProviderToolFlow and TransformMessages converted every message from the type-switch copy back to the Message interface, which allocates a copy of the whole message on every request, and prepareProviderToolFlow allocated a fresh map per assistant turn. A message that needs no change now keeps the interface value it arrived as, and the tool-flow bookkeeping is cleared instead of reallocated.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
532f5728bb(2026-10-10),942ecc33e7(2026-10-10),e9392fd158(2026-10-10) - Tests:
ai/google_empty_contents_test.go::TestGoogleEmptyContentsChecksThePayloadOnPayloadReturned,ai/google_empty_contents_test.go::TestGoogleEmptyContentsFailsBeforeSending,ai/tool_flow_result_reset_test.go::TestTransformMessagesResultBeforeCallDoesNotAnswerIt,ai/transform_messages_allocs_test.go::TestTransformMessagesDoesNotReboxUnchangedMessages,test/pipeline-diff/system_only_test.go::TestSystemOnlyHistoryFailsBeforeSendingLikePi - Release note: A Google Generative AI or Vertex request with no
contents(a history of system messages only, or one user message without content) now fails withcontents are requiredbefore it is sent, as Pi's@google/genaiSDK does, instead of reaching the API and failing with HTTP 400. AnonPayloadhook that adds or removescontentsis checked as sent.
BF-e973464
A fused Go extension's createLocalBashOperations().exec (and createLocalPowerShellOperations) no longer…
- Source: PiG's own defect
- Symptom: A fused Go extension's
createLocalBashOperations().exec(andcreateLocalPowerShellOperations) no longer fails with "extension stopped while waiting for builtinTool.localExec response" when another extension in the same process stops while the command runs; a command past itstimeoutrejects with Pi'stimeout:<seconds>. - Root cause: localShellExec has no Context, so it sent builtinTool.localExec over runningExtension()'s connection: the first running extension in the process. A fused extension shares the process with every other fused extension, so a command with a timeout ended with "extension stopped while waiting for builtinTool.localExec response" when that extension stopped while the command ran, where Pi's exec rejects with "timeout:<seconds>" and depends on no extension.
- User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: 0.5.3
- Fix commits:
e97346436d(2026-10-22) - Tests:
coding/extension/host/subprocess/local_exec_test.go::TestLocalExecReportsPisTimeoutAndEndsWithADeliveryFailure,test/extension-conformance/builtin_local_exec_fused_test.go::TestConformance_FusedLocalExecIsNotCarriedByAnotherExtension - Release note: A fused Go extension's
createLocalBashOperations().exec(andcreateLocalPowerShellOperations) no longer fails with "extension stopped while waiting for builtinTool.localExec response" when another extension in the same process stops while the command runs; a command past itstimeoutrejects with Pi'stimeout:<seconds>. The call has noContext, so it was sent over the connection of the first running extension in the process, and ended with that extension, whichever made the call. The host now runs the command for a fused extension directly, as it already does for the other helpers that have noContext, so the command depends on no extension, as Pi's exec depends on none.
BF-e9e9cf8
An extension that calls a pi-ai function such as completeSimple at top level or in its factory no longer…
- Source: PiG's own defect
- Symptom: An extension that calls a pi-ai function such as
completeSimpleat top level or in its factory no longer hangs startup; the Host answers the model stream while the module loads. - Root cause: An extension that awaited completeSimple at top level or in its factory hung PiG: the Node runtime sent the modelStream host call before it had registered, and the Host answered extension calls only after the register handshake the factory was holding up. Pi runs pi-ai in the extension's own process with no registration gate.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
15cb4f2db8(2026-10-11),e9e9cf8816(2026-10-11) - Tests:
coding/cli/extension_host_wiring_binary_test.go::TestHeadlessExtensionSessionActionsMatchPi,coding/extension/host/subprocess/runtime_node_model_runtime_test.go::TestNodeLoadTimeCompletionAnswersBeforeRegistration,internal/codingagent/subprocess_early_model_stream_test.go::TestEarlyModelStreamServesAPICallsWithoutASession,test/extension-conformance/host_parser_helpers_test.go::TestConformance_HostParserHelpersAgreeAcrossSDKs - Release note: An extension that calls a pi-ai function such as
completeSimpleat top level or in its factory no longer hangs startup; the Host answers the model stream while the module loads.
BF-ea6db99
A provider request that fails in transport now records its cause: the failed message carries a…
- Source: PiG's own defect
- Symptom: A provider request that fails in transport now records its cause: the failed message carries a
provider_transport_failurediagnostic (the same shape Pi writes for its Codex WebSocket failures) with the Go cause and a Node-style code such asECONNRESET,ETIMEDOUTorENOTFOUND, so a reset, a dial timeout and a DNS failure can be told apart in the session file and/bug. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ea6db9948d(2026-10-14) - Tests:
ai/transport_diagnostic_test.go::TestClassifyTransportCause,ai/transport_diagnostic_test.go::TestOldAssistantMessageWithoutDiagnosticRoundTrips,ai/transport_diagnostic_test.go::TestProviderContextCancelRecordsNoTransportDiagnostic,ai/transport_diagnostic_test.go::TestProviderTransportMidStreamRecordsDiagnostic,ai/transport_diagnostic_test.go::TestProviderTransportResetRecordsDiagnosticAndKeepsMessage,ai/transport_diagnostic_test.go::TestTransportDiagnosticLeavesEventStreamUnchanged,ai/transport_diagnostic_test.go::TestTransportFailureDiagnosticSkipsAbortsAndOtherErrors - Release note: A provider request that fails in transport now records its cause: the failed message carries a
provider_transport_failurediagnostic (the same shape Pi writes for its Codex WebSocket failures) with the Go cause and a Node-style code such asECONNRESET,ETIMEDOUTorENOTFOUND, so a reset, a dial timeout and a DNS failure can be told apart in the session file and/bug. The error message, stop reason, retries and events are unchanged, and existing sessions are untouched. Reported by @ShoichiTect (#87)
BF-ea90fb3
A running !cmd block keeps only the last 2000 lines or 50 KB it shows instead of every byte the command…
- Source: PiG's own defect
- Symptom: A running
!cmdblock keeps only the last 2000 lines or 50 KB it shows instead of every byte the command prints, so a long build or!catof a large file no longer grows memory with its output (32 MB of output: 33.3 MB retained before, 0.2 MB now). - Root cause: Records D176 and the changelog fragment.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
0532a57c08(2026-10-13),ea90fb311c(2026-10-13) - Tests:
internal/codingagent/bash_execution_oracle_test.go::TestBashExecutionComponentMatchesPi,internal/codingagent/settings_live_effects_test.go::TestOnSettingAppliedOutputPaddingReachesEveryChatAndPendingChild,tui/bash_execution_retention_test.go::TestBashExecutionStreamingRetainsTheTailNotTheWholeOutput,tui/bash_execution_test.go::TestBashExecutionAppendOutputCleansChunksAndExposesOutputAndCommand,tui/component_accessors_test.go::TestBashExecutionAppendOutputSanitizesAndGettersReadItBack - Release note: A running
!cmdblock keeps only the last 2000 lines or 50 KB it shows instead of every byte the command prints, so a long build or!catof a large file no longer grows memory with its output (32 MB of output: 33.3 MB retained before, 0.2 MB now). The rows drawn and the output saved to the session are unchanged.tui.BashExecutionComponent.GetOutputis replaced byContextOutput, which returns the shown window.
BF-ead285d
Two compact commands of one RPC input batch no longer cancel each other; they run one after another (D147)
- Source: PiG's own defect
- Symptom: Two
compactcommands of one RPC input batch no longer cancel each other; they run one after another (D147). - Root cause: Each compact ran on its own goroutine and the second claimed the Session while the first was starting: 'Compaction cancelled' in 2 of 3 runs. The compactions now run through rpcAwaitInOrder, the same chain as set_model and the Session replacements, each starting after the previous one settled.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ead285ddfa(2026-10-10) - Tests:
coding/cli/rpc_replacement_order_test.go::TestRPCCompactsOfOneBatchDoNotCancelEachOther - Release note: Two
compactcommands of one RPC input batch no longer cancel each other; they run one after another (D147).
BF-eb34085
The find tool trims each fd result line like JavaScript trim(): a name ending in U+FEFF loses it and a name…
- Source: PiG's own defect
- Symptom: The find tool trims each fd result line like JavaScript trim(): a name ending in U+FEFF loses it and a name ending in U+0085 keeps it.
- Root cause: find.ts reads each fd line as rawLine.replace(/\r$/, "").trim(); Go used strings.TrimSpace, which trims U+0085 and keeps U+FEFF where JavaScript does the opposite, so a file name ending in U+FEFF kept it and one ending in U+0085 or a name that is only U+0085 lost it. The line is trimmed with jsstring.Trim now. The grep and find stderr trims and the grep blank-line test see no such text (ripgrep JSON lines, error messages) and are left alone.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
eb34085200(2026-10-11) - Tests:
internal/codingagent/tools/find_trim_oracle_test.go::TestFindToolTrimsFdLinesLikePi - Release note: The find tool trims each fd result line like JavaScript trim(): a name ending in U+FEFF loses it and a name ending in U+0085 keeps it.
BF-ebb4c0e
An "m" operation whose permutation a Go caller builds as []int validates and applies again, as it did in…
- Source: PiG's own defect
- Symptom:
AssertValidOpreportedm permutation is not an array. - Root cause:
- An "m" permutation built as []int (Pi's number[]) validated and applied in 0.4.1; since the one-package change it fails with "m permutation is not an array" and Apply panics on it. permutationItems accepts both forms. TestAnIntPermutationStillApplies is red before the fix. - Since Op is the alias again, json.Unmarshal into durable.DocumentContent put Go maps in its operations ( decoded them in order). DocumentContent.UnmarshalJSON decodes Ops as delta.Ops, as Pi's storage JSON.parse keeps order (storage/sqlite/storage.ts:63). TestDocumentContentDecodesOperationObjectsInKeyOrder is red before. - Decoding the node facet host's "state" Ops as a plain []chord.Op survived every test…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
80988ccdf3(2026-10-07),1f5a782c0a(2026-10-08),2f86645d6b(2026-10-08),33c35623be(2026-10-08),5355335e3a(2026-10-08),6ca5fde3a9(2026-10-08),9d34ce6e4f(2026-10-08),301e3c185f(2026-10-09),8c7bacff5b(2026-10-09),9b505ead89(2026-10-09),ebb4c0e638(2026-10-09) - Tests:
chord/delta/apply_immutable_upstream_test.go::TestCheckedImmutableOperationApplication,chord/delta/apply_immutable_upstream_test.go::TestImmutableApplicationComplexity,chord/delta/apply_numeric_key_test.go::TestApplyImmutableSetsANumericSegmentOnAnObjectAsAKey,chord/delta/apply_pi_oracle_test.go::TestApplyResolvesArraySegmentsInUpstreamOrder,chord/delta/apply_pi_oracle_test.go::TestTruncateInsideASurrogatePairKeepsTheLowHalf,chord/delta/compat_test.go::TestAnIntPermutationStillApplies,chord/delta/compat_test.go::TestOpsDecodeKeepsObjectKeyOrder,chord/delta/compat_test.go::TestPathOfAReplacementIsNiland 30 more in docs/bug-ledger.json - Release note: An
"m"operation whose permutation a Go caller builds as[]intvalidates and applies again, as it did in 0.4.1. Before this fix,AssertValidOpreportedm permutation is not an array.
BF-ebb4c0e-2
durable.DocumentContent decoded by encoding/json keeps the key order of the objects its operations carry…
- Source: PiG's own defect
- Symptom:
durable.DocumentContentdecoded byencoding/jsonkeeps the key order of the objects its operations carry, as Pi's storageJSON.parsedoes. - Root cause:
- An "m" permutation built as []int (Pi's number[]) validated and applied in 0.4.1; since the one-package change it fails with "m permutation is not an array" and Apply panics on it. permutationItems accepts both forms. TestAnIntPermutationStillApplies is red before the fix. - Since Op is the alias again, json.Unmarshal into durable.DocumentContent put Go maps in its operations ( decoded them in order). DocumentContent.UnmarshalJSON decodes Ops as delta.Ops, as Pi's storage JSON.parse keeps order (storage/sqlite/storage.ts:63). TestDocumentContentDecodesOperationObjectsInKeyOrder is red before. - Decoding the node facet host's "state" Ops as a plain []chord.Op survived every test…
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6ca5fde3a9(2026-10-08),301e3c185f(2026-10-09),8c7bacff5b(2026-10-09),ac3759ebb7(2026-10-09),cb15f9d6b6(2026-10-09),ebb4c0e638(2026-10-09) - Tests:
chord/delta/apply_immutable_upstream_test.go::TestCheckedImmutableOperationApplication,chord/delta/apply_immutable_upstream_test.go::TestImmutableApplicationComplexity,chord/delta/apply_numeric_key_test.go::TestApplyImmutableSetsANumericSegmentOnAnObjectAsAKey,chord/delta/apply_pi_oracle_test.go::TestApplyResolvesArraySegmentsInUpstreamOrder,chord/delta/apply_pi_oracle_test.go::TestTruncateInsideASurrogatePairKeepsTheLowHalf,chord/delta/compat_test.go::TestAnIntPermutationStillApplies,chord/delta/compat_test.go::TestOpsDecodeKeepsObjectKeyOrder,chord/delta/compat_test.go::TestPathOfAReplacementIsNiland 41 more in docs/bug-ledger.json - Release note:
durable.DocumentContentdecoded byencoding/jsonkeeps the key order of the objects its operations carry, as Pi's storageJSON.parsedoes.
BF-ebbdfe7
fix(extensions): load highlight.js grammars after the startup session_start, as Pi does
- Source: PiG's own defect
- Symptom: fix(extensions): load highlight.js grammars after the startup session_start, as Pi does
- Root cause: Pi's interactive mode starts loadAllHighlightLanguages only after init has emitted session_start and resources_discover and rendered (interactive-mode.ts:1100-1122). The Node runtime started it when the host's ready frame arrived, so the dynamic import evaluated every highlight.js grammar (~100 ms of main-thread CPU, node --cpu-prof) just as the host sent session_start, and the extension's session_start handler waited behind it. On 06-eventbus-pubsub (warm, Linux, not a BENCH) the host's session_start emission took ~88 ms; with this change ~38 ms.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
ebbdfe743e(2026-10-11) - Tests:
coding/extension/host/subprocess/node_highlight_startup_test.go::TestNodeRuntimeLoadsHighlightLanguagesAfterStartupSessionStart,coding/extension/host/subprocess/node_highlight_startup_test.go::TestNodeRuntimeLoadsHighlightLanguagesAtReadyWithoutStartupHandlers
BF-ebec8e2
A URL host with an xn-- label that is empty after IDNA mapping (ws://xn--。, https://..\ufeffxn--) or…
- Source: PiG's own defect
- Symptom: A URL host with an
xn--label that is empty after IDNA mapping (ws://xn--。,https://..\ufeffxn--) or that holds non-ASCII text after the prefix (https://xn--é/) is rejected, as Node'snew URLdoes; the first was read as a dropped label, the second re-encoded. - Root cause: A random composition of URL tokens compared with Node's new URL (20,000 strings, plus 300,000-string seed sweeps) found hosts Node rejects and nodeurl accepted: - a label that is only xn-- after mapping: U+3002/U+FF0E/U+FF61 separators, ignored code points (U+FEFF, soft hyphen, variation selectors) and compatibility forms hid it from the plain-dot split in hasEmptyPunycodeLabel; - an xn-- label with non-ASCII text, which x/net/idna re-encoded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ebec8e29b9(2026-10-11) - Tests:
internal/nodeurl/random_compose_oracle_test.go::TestParseMatchesNodeURLOnComposedInputs,internal/nodeurl/random_compose_oracle_test.go::TestParseMatchesNodeURLOnInvalidPunycodeLabels - Release note: A URL host with an
xn--label that is empty after IDNA mapping (ws://xn--。,https://..\ufeffxn--) or that holds non-ASCII text after the prefix (https://xn--é/) is rejected, as Node'snew URLdoes; the first was read as a dropped label, the second re-encoded.
BF-ebfeed0
getSystemPromptOptions().hiddenTools lists hidden tools in the order the prepareLoadout hooks hid them…
- Source: PiG's own defect
- Symptom:
getSystemPromptOptions().hiddenToolslists hidden tools in the order theprepareLoadouthooks hid them, as Pi does, instead of sorted. - Root cause: PiG sorted the names, so getSystemPromptOptions().hiddenTools and the before_agent_start options listed them in a different order.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ebfeed0965(2026-10-05) - Tests:
coding/session_hidden_tools_order_test.go::TestSystemPromptOptionsListHiddenToolsInTheOrderTheHooksHidThem - Release note:
getSystemPromptOptions().hiddenToolslists hidden tools in the order theprepareLoadouthooks hid them, as Pi does, instead of sorted.
BF-ec2080a
A subprocess extension's ctx.ui.custom component now learns when another component shown over it takes…
- Source: PiG's own defect
- Symptom: The component kept reporting focus until its next key arrived.
- Root cause: TestCustomOverlayFocusChangesReachSDKs (bridge in place, SDK changes and pack reverted): every cell (subprocess-go, -node, -node-packed, -rust, -python, fused-go) "the component did not render focused=false after it lost focus".
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
007c91dcbb(2026-10-13),1848633092(2026-10-13),2a055a7a3a(2026-10-13),d87b70c54c(2026-10-13),ec2080a75f(2026-10-13) - Tests:
coding/extension/host/subprocess/host_calls_test.go::TestReadLoopWidgetPushOrdersAgainstQueuedSlotCalls,coding/extension/host/subprocess/ui_bridge_test.go::TestFocusedCustomComponentsStack,coding/extension/host/subprocess/ui_bridge_test.go::TestInteractiveDialogOpensOverFocusedOverlay,coding/extension/host/subprocess/ui_prompt_test.go::TestUIBridgePromptScopeDialogOverCustomAndNoUI,coding/extension/host/subprocess/view_terminal_parity_test.go::TestViewsLeaveTheTerminalBytesUnchanged,coding/extension/host/subprocess/widget_key_namespace_test.go::TestTwoExtensionsSettingOneWidgetKeyShareIt,extensions/sdk-py/tests/test_system_prompt_render.py,extensions/sdk/system_prompt_render_test.go::TestBeforeAgentStartSystemPromptRenderErrorIsTheReadersErrorand 15 more in docs/bug-ledger.json - Release note: A subprocess extension's
ctx.ui.customcomponent now learns when another component shown over it takes focus and when focus comes back, as Pi'ssetFocustells it: the overlay handle'sisFocused()follows, the component'sfocusedflag is cleared and set again (Node, GoSetFocused, Pythonfocused) and the component renders again. This holds for Node, Go, Rust and Python extensions. Before, the component kept reporting focus until its next key arrived.
BF-ed05099
An extension's ui.setHiddenThinkingLabel now changes the label shown for hidden thinking in the chat…
- Source: PiG's own defect
- Symptom: An extension's
ui.setHiddenThinkingLabelnow changes the label shown for hidden thinking in the chat, including assistant messages already on screen. - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ed05099398(2026-10-06) - Tests:
ai/pi_messages_event_type_test.go::TestPiMessagesEventTypeConstantsMatchTheWireLiterals,coding/extension/session_boundary_draft_type_test.go::TestSessionBoundaryDraftTypeSelectsTheUpstreamMember,internal/codingagent/interactive_resume_thinking_test.go::TestInteractiveModeSetHiddenThinkingLabelReachesExistingAndLaterBlocks,internal/codingagent/reload_trust_models_test.go::TestInitNoticesFollowUpstreamOrder,tui/assistant_message_block_label_test.go::TestAssistantMessageComponentSetHiddenThinkingLabel,tui/assistant_message_block_test.go::TestAssistantMessageBlock_SetHideThinkingBlock,tui/assistant_mouse_upstream_test.go::TestAssistantThinkingClicksThroughTerminal,tui/assistant_thinking_markdown_test.go::TestAssistantThinkingTransformIsSeparateAndTracksStateand 6 more in docs/bug-ledger.json - Release note: An extension's
ui.setHiddenThinkingLabelnow changes the label shown for hidden thinking in the chat, including assistant messages already on screen. It only changed a footer string before.
BF-ed05099-2
Tab at the start of a line no longer forces file completion while the text before the cursor is a…
- Source: PiG's own defect
- Symptom: Tab at the start of a line no longer forces file completion while the text before the cursor is a slash-command name.
- Root cause: Not recorded.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ed05099398(2026-10-06) - Tests:
ai/pi_messages_event_type_test.go::TestPiMessagesEventTypeConstantsMatchTheWireLiterals,coding/extension/session_boundary_draft_type_test.go::TestSessionBoundaryDraftTypeSelectsTheUpstreamMember,internal/codingagent/interactive_resume_thinking_test.go::TestInteractiveModeSetHiddenThinkingLabelReachesExistingAndLaterBlocks,internal/codingagent/reload_trust_models_test.go::TestInitNoticesFollowUpstreamOrder,tui/assistant_message_block_label_test.go::TestAssistantMessageComponentSetHiddenThinkingLabel,tui/assistant_message_block_test.go::TestAssistantMessageBlock_SetHideThinkingBlock,tui/assistant_mouse_upstream_test.go::TestAssistantThinkingClicksThroughTerminal,tui/assistant_thinking_markdown_test.go::TestAssistantThinkingTransformIsSeparateAndTracksStateand 6 more in docs/bug-ledger.json - Release note: Tab at the start of a line no longer forces file completion while the text before the cursor is a slash-command name.
BF-ed05099-3
pi.exec called while an extension loads, before the session binds, no longer fails with `extension…
- Source: PiG's own defect
- Symptom:
pi.execcalled while an extension loads, before the session binds, no longer fails withextension connection closed or replaced. - Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ed05099398(2026-10-06) - Tests:
ai/pi_messages_event_type_test.go::TestPiMessagesEventTypeConstantsMatchTheWireLiterals,coding/extension/session_boundary_draft_type_test.go::TestSessionBoundaryDraftTypeSelectsTheUpstreamMember,internal/codingagent/interactive_resume_thinking_test.go::TestInteractiveModeSetHiddenThinkingLabelReachesExistingAndLaterBlocks,internal/codingagent/reload_trust_models_test.go::TestInitNoticesFollowUpstreamOrder,tui/assistant_message_block_label_test.go::TestAssistantMessageComponentSetHiddenThinkingLabel,tui/assistant_message_block_test.go::TestAssistantMessageBlock_SetHideThinkingBlock,tui/assistant_mouse_upstream_test.go::TestAssistantThinkingClicksThroughTerminal,tui/assistant_thinking_markdown_test.go::TestAssistantThinkingTransformIsSeparateAndTracksStateand 6 more in docs/bug-ledger.json - Release note:
pi.execcalled while an extension loads, before the session binds, no longer fails withextension connection closed or replaced. Load-time exec runs with Pi's default working directory, as Pi's loader serves it (#165, reported by @luiul).
BF-ed05099-4
Tool-call input keeps its object members in the order the model sent them through events, tool execution…
- Source: PiG's own defect
- Symptom: Tool-call input keeps its object members in the order the model sent them through events, tool execution,
tool_resultand the session file, where an edited input was sorted by key (#165). - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ed05099398(2026-10-06) - Tests:
ai/pi_messages_event_type_test.go::TestPiMessagesEventTypeConstantsMatchTheWireLiterals,coding/extension/session_boundary_draft_type_test.go::TestSessionBoundaryDraftTypeSelectsTheUpstreamMember,internal/codingagent/interactive_resume_thinking_test.go::TestInteractiveModeSetHiddenThinkingLabelReachesExistingAndLaterBlocks,internal/codingagent/reload_trust_models_test.go::TestInitNoticesFollowUpstreamOrder,tui/assistant_message_block_label_test.go::TestAssistantMessageComponentSetHiddenThinkingLabel,tui/assistant_message_block_test.go::TestAssistantMessageBlock_SetHideThinkingBlock,tui/assistant_mouse_upstream_test.go::TestAssistantThinkingClicksThroughTerminal,tui/assistant_thinking_markdown_test.go::TestAssistantThinkingTransformIsSeparateAndTracksStateand 6 more in docs/bug-ledger.json - Release note: Tool-call input keeps its object members in the order the model sent them through events, tool execution,
tool_resultand the session file, where an edited input was sorted by key (#165).
BF-edb73db
fix(codingagent): one owner-overflow forwarder at a time; the drain test waits for it, not for the flag's…
- Source: PiG's own defect
- Symptom: fix(codingagent): one owner-overflow forwarder at a time; the drain test waits for it, not for the flag's early read
- Root cause: forwardOwnerOverflow removed the last task, released the lock, and only then took it again to see the overflow empty and clear ownerOverflowActive. A post the owner loop made in that gap (its queue still full) found the overflow empty, started a second forwarder and queued its task; both forwarders then sent the same task, which ran twice, and the second popped an empty overflow (index out of range in a background goroutine). The forwarder now leaves in the critical section that removes the last task and clears the flag, so a post that finds the overflow empty finds no forwarder.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
edb73db2bf(2026-10-14) - Tests:
internal/codingagent/overlay_owner_loop_deadlock_test.go::TestOwnerOverflowRunsEachPostOnceInOrderWhileTheQueueStaysFull,internal/codingagent/overlay_owner_loop_deadlock_test.go::TestRunOnMainFromOwnerLoopNeverBlocksOnItsOwnQueue
BF-edd287c
Migrating a version 1 session file that holds a line parsing to a number, string, array or true no longer…
- Source: PiG's own defect
- Symptom: Migrating a version 1 session file that holds a line parsing to a number, string, array or
trueno longer fails while finding the header. - Root cause: Pi keeps whatever JSON.parse returns for a session-file line (session-manager.ts parseSessionEntryLine). migrateV1ToV2 gives every record after the header an id and a parent: an array takes the id as an unserialized property and the chain goes through it, a number, string or boolean throws the strict-mode TypeError.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
34f7594aa5(2026-10-11),edd287c5d8(2026-10-12) - Tests:
internal/codingagent/extension_ui_phase_shutdown_test.go::TestAwaitedExtensionPhaseWithoutShutdownReturnsItsResult,internal/codingagent/extension_ui_phase_shutdown_test.go::TestNewSessionEndedByShutdownIsNotAFatalRuntimeError,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseDoesNotWaitForAHandlerThatIgnoresCancellation,internal/codingagent/extension_ui_phase_shutdown_test.go::TestShutdownDuringAwaitedExtensionPhaseEndsThePhase,internal/codingagent/session_open_nonentry_oracle_test.go::TestMigrateSessionEntriesPassesOverRecordsThatAreNotObjectsWhenFindingTheHeader,internal/codingagent/session_open_nonentry_oracle_test.go::TestMigrateV1ToV2ChainsThroughAnArrayRecordAndRefusesAPrimitiveAsPiDoes,internal/codingagent/session_open_nonentry_oracle_test.go::TestSessionOpenKeepsRecordsThatAreNotEntriesAsPiDoes,test/parity/scenarios/extensions-runtime/73-shutdown-from-session-before-fork.tomland 2 more in docs/bug-ledger.json - Release note: Migrating a version 1 session file that holds a line parsing to a number, string, array or
trueno longer fails while finding the header. Like Pi, an array record stays as written and still takes a place in the migration's id chain, and a number, string or boolean record stops the migration with the sameCannot create property 'id'error Pi throws.
BF-eeac307
Anthropic and Bedrock assistant messages write providerThinkingLevel after model, and write…
- Source: PiG's own defect
- Symptom: Anthropic and Bedrock assistant messages write
providerThinkingLevelaftermodel, and writecacheWrite1handreasoningaftercostinusage, as Pi does. - Root cause: Pi passes a tool's parameters object through to the body (openai-completions.ts convertTools :1506-1514, openai-responses-shared.ts :386-390), so JSON.stringify writes the schema's members in declaration order; a strict schema is the same object rebuilt member by member (constrained-sampling.ts makeJsonSchemaNodeStrict: required, then additionalProperties are assigned after the existing members, and an optional property becomes {anyOf: [property, {type: "null"}]}). PiG copied ToolSchema.Parameters as a map[string]any and dropped the recorded parameterOrder, so every schema object was written in sorted order.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2309604d80(2026-10-10),c10cde302d(2026-10-10),eeac307095(2026-10-10),f29e906cb3(2026-10-10) - Tests:
agent/assistant_member_order_oracle_test.go::TestAssistantLateMembersFollowPiCreationOrder,ai/constrained_sampling_oracle_test.go::TestConstrainedSamplingMatchesPi,ai/openai_request_key_order_oracle_test.go::TestOpenAICompletionsRequestKeysFollowPiAssignmentOrder,ai/openai_responses_request_key_order_oracle_test.go::TestOpenAIResponsesRequestKeysFollowPiAssignmentOrder,coding/cli/rpc_assistant_member_order_test.go::TestRPCAssistantMessageMembersMatchTheSessionLineOrder,coding/cli/rpc_model_type_binary_test.go::TestRPCModelObjectsCarryTheRegistryModelType,coding/codemode_session_upstream_test.go::TestUpstreamCodemodeModels,coding/session_boundaries_drafts_upstream_test.go::TestUpstreamSessionBoundariesDrafts - Release note: Anthropic and Bedrock assistant messages write
providerThinkingLevelaftermodel, and writecacheWrite1handreasoningaftercostinusage, as Pi does.
BF-ef0f95a
OpenCode Zen and OpenCode Go use the opencode.ai/inference endpoints, which fixes OpenCode Go Gemini models…
- Source: PiG's own defect
- Symptom: OpenCode Zen and OpenCode Go use the
opencode.ai/inferenceendpoints, which fixes OpenCode Go Gemini models failing with a 404; OpenCode Go MiniMax M2.7 uses the Anthropic Messages API, and OpenCode Go DeepSeek V4 Flash is no longer listed (carried Pi fix, Pi 195120a9). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ef0f95ab0e(2026-10-14) - Tests:
ai/classifier_models_upstream_test.go::TestClassifierModelsUpstream,cmd/gen-models/carried_fixes_test.go::TestCarriedOpenCodeInferenceEndpoints - Release note: OpenCode Zen and OpenCode Go use the
opencode.ai/inferenceendpoints, which fixes OpenCode Go Gemini models failing with a 404; OpenCode Go MiniMax M2.7 uses the Anthropic Messages API, and OpenCode Go DeepSeek V4 Flash is no longer listed (carried Pi fix, Pi 195120a9).
BF-efded13
Installing a Package or Piglet from a local git:file:// source on Windows no longer fails with `fatal…
- Source: PiG's own defect
- Symptom: Installing a Package or Piglet from a local
git:file://source on Windows no longer fails withfatal: '$GIT_DIR' too big. - Root cause: A file:// source checked out to <root>/localhost/<whole absolute path>, doubling its length; Git for Windows then failed with "$GIT_DIR too big" whatever core.longpaths says. A file:// source now checks out to <host>/<12 hex of sha256(absolute path)>/<basename>. Sources Pi accepts keep Pi's layout. Older nested file:// checkouts are not reused.
- User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: 0.5.3
- Fix commits:
efded13767(2026-10-22) - Tests:
coding/cli/git_checkout_path_test.go::TestGitCheckoutPathOnThisPlatform,coding/cli/git_checkout_path_test.go::TestGitCheckoutRelativePerPlatform,internal/packagemanager/git_checkout_relative_test.go::TestGitCheckoutRelativeFileSourceIsShort,internal/packagemanager/git_checkout_relative_test.go::TestGitCheckoutRelativeFileSourceLengthBound,internal/packagemanager/git_checkout_relative_test.go::TestGitCheckoutRelativeNonFileKeepsPiLayout,internal/packagemanager/git_checkout_relative_test.go::TestGitCheckoutRelativeSameBasenameDiffers,internal/packagemanager/git_checkout_relative_test.go::TestGitCheckoutRelativeWindowsFileSourceUnderScratchRootIsShort - Release note: Installing a Package or Piglet from a local
git:file://source on Windows no longer fails withfatal: '$GIT_DIR' too big. A file URL now checks out to<install root>/localhost/<12 hex of the path's sha256>/<basename>instead of re-nesting the whole absolute path, which pushed$GIT_DIRpast Git for Windows' own path limit thatcore.longpathsdoes not lift. Sources Pi accepts keep Pi's exact layout. A file URL checkout made by an earlier version is not reused and is cloned again.
BF-f02269c
When an extension's before_provider_request handler replaces an Anthropic request, or a Codex WebSocket…
- Source: PiG's own defect
- Symptom: When an extension's
before_provider_requesthandler replaces an Anthropic request, or a Codex WebSocket request carries raw JSON, nested values are written as Pi'sJSON.stringifywrites them afterJSON.parse:\u00e9becomesé, and a nested object's repeated key keeps its first place with its last value. - Root cause: One part of is still missing there: writeJSONObjectMembers copied each value's text (json.Compact). Pi sends JSON.stringify({ ...nextParams, stream: true }) of what JSON.parse read (anthropic-messages.ts:640-642), so
\u00e9goes out asé, and a nested repeated key keeps its first place with its last value. writeJSONObjectMembers now writes each value through jsonstringify.AppendCanonical. The Codex WebSocket writer shares it; a body from marshalRequestBody is already canonical and takes the copy path. - User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f02269c6e1(2026-10-10) - Tests:
ai/anthropic_replaced_payload_order_oracle_test.go::TestAnthropicReplacedPayloadKeepsPiMemberOrder - Release note: When an extension's
before_provider_requesthandler replaces an Anthropic request, or a Codex WebSocket request carries raw JSON, nested values are written as Pi'sJSON.stringifywrites them afterJSON.parse:\u00e9becomesé, and a nested object's repeated key keeps its first place with its last value. PiG copied the replacement's text unchanged.
BF-f06ab7e
A run that sendMessage({ triggerTurn: true }) starts while idle emits before_agent_start, checks…
- Source: PiG's own defect
- Symptom: A run that
sendMessage({ triggerTurn: true })starts while idle emitsbefore_agent_start, checks compaction first, applies the handlers' result and delivers pendingnextTurnmessages. - Root cause: PiG did this only for idle triggerTurn custom messages (D139) and prepared a user prompt idle.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1cc200dcb5(2026-10-12),4c36280562(2026-10-13),f06ab7eca6(2026-10-13) - Tests:
agent/abort_before_first_request_test.go::TestAbortBeforeTheFirstRequestSendsNoProviderRequest,coding/session_pi_bugs_test.go::TestCustomRunStartThatLosesTheClaimRejectsWithTheAgentError,coding/session_preflight_test.go::TestAbandonedRunStartReturnsTheSessionToIdle,coding/session_preflight_test.go::TestPromptRunStartClaimsTheSessionBeforePreparation,coding/session_prompt_abort_starting_upstream_test.go::TestUpstreamSessionPromptAbortWhileBeforeAgentStartRuns,coding/session_prompt_custom_run_upstream_test.go::TestUpstreamSessionPromptRunsStartedByACustomMessage,coding/session_prompt_start_run_test.go::TestUserPromptRunIsBusyWhileItStarts - Release note: A run that
sendMessage({ triggerTurn: true })starts while idle emitsbefore_agent_start, checks compaction first, applies the handlers' result and delivers pendingnextTurnmessages. The event carriesmessageType("user"or"custom") and, for a custom run,message. A user prompt's run, like a custom one, is busy from its start, as in Pi 1.2.0: a message sent while its compaction check andbefore_agent_starthandlers run joins it, a prompt orsendUserMessagesent meanwhile must namestreamingBehavior/deliverAs, bash results recorded meanwhile are appended after the handlers, and an abort aborts the run atagent_startinstead of letting its request complete (Pi #5581, Pi PR #10739).
BF-f07d033
A response stream dropped mid-run now reports terminated (cause: ...) instead of a bare terminated (D144…
- Source: A Pi defect that PiG fixed first (divergence D144)
- Symptom: A response stream dropped mid-run now reports
terminated (cause: ...)instead of a bareterminated(D144, Pi #10697). - Root cause: A user cannot tell a server disconnect from a client timeout without instrumenting the process.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10697
- Removed when Pi fixes it: Pi includes
error.causein the message of a transport failure. - Fix commits:
f07d033aac(2026-10-10) - Tests:
ai/transport_error_test.go::TestProviderTransportFailureMidStreamKeepsTheCause,ai/transport_error_test.go::TestProviderTransportFailureMidStreamMatchesTerminated - Release note: A response stream dropped mid-run now reports
terminated (cause: ...)instead of a bareterminated(D144, Pi #10697).
BF-f0b910f
Extensions' ui_prompt_start and ui_prompt_end notifications are recorded in order when the host is slow…
- Source: PiG's own defect
- Symptom: Extensions'
ui_prompt_startandui_prompt_endnotifications are recorded in order when the host is slow to apply the start event'sctx.ui.notify: the dialog's answer is no longer let through, and the end event no longer sent, before the handler's own host call has applied. - Root cause: A runtime reports the suspension of a handler that waits for a host call before it sends the call. A start notification applied late (a stalled goroutine on a loaded machine) was recorded after the answers settled and the end event's notification, where Pi runs each handler's synchronous part before the prompting extension continues (TestConformance_ConcurrentDialogsAgreeAcrossSDKs/fused-go/last_in_first_out failed once in three full runs: the first ui_prompt event was the end).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.3
- Fix commits:
f0b910f8ca(2026-10-22) - Tests:
coding/extension/host/subprocess/ack_hold_test.go::TestBlockedAcknowledgmentWaitsForTheAnnouncedHostCall,test/extension-conformance/ui_concurrent_dialogs_test.go::TestConformance_ConcurrentDialogsAgreeAcrossSDKs,test/extension-conformance/ui_concurrent_dialogs_test.go::TestConformance_UIPromptStartIsAppliedBeforeTheDialogsAnswerSettles - Release note: Extensions'
ui_prompt_startandui_prompt_endnotifications are recorded in order when the host is slow to apply the start event'sctx.ui.notify: the dialog's answer is no longer let through, and the end event no longer sent, before the handler's own host call has applied. A blocked-on-a-host-call report from an extension runtime admitted the UI prompt drain as soon as it was read, while the call it announced was still queued; the admission now waits for that call.
BF-f12896b
Interactive startup writes the terminal color query before its first frame, as Pi does: starting the renderer…
- Source: PiG's own defect
- Symptom: Interactive startup writes the terminal color query before its first frame, as Pi does: starting the renderer only requests the first frame, which paints while startup waits for the terminal's colors.
- Root cause: Pi's TUI.start() requests the first frame instead of painting it, and init() writes the color query in applyFromSettings() before the deferred frame runs at await waitForTerminalColors(). Init now mounts the layout, starts the renderer with a requested frame (TuiAltScreen.StartRequestingRender in fullscreen), and the color wait serves scheduled renders, so the frame paints during the wait. A live tui-mode switch still paints at once.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f12896bcc6(2026-10-13) - Tests:
internal/codingagent/interactive_init_state_pty_test.go::TestInitBuildsTheInteractiveStateBeforeTheSessionBindChild,internal/codingagent/startup_color_wait_render_test.go::TestRenderRequestedWhileStartupWaitsForTheTerminalColorsPaints,internal/codingagent/startup_query_order_pty_test.go::TestStartupWritesTheTerminalColorQueryBeforeTheFirstFrame,internal/codingagent/startup_query_order_pty_test.go::TestStartupWritesTheTerminalColorQueryBeforeTheFirstFrameChild - Release note: Interactive startup writes the terminal color query before its first frame, as Pi does: starting the renderer only requests the first frame, which paints while startup waits for the terminal's colors. In fullscreen the alternate screen is still entered first. The color round trip is now under way when the editor first shows, so input typed at the first frame waits on the same startup work as in Pi.
BF-f19efb1
A session_compact handler's passive custom message reaches the overflow retry (D138, Pi #9051)
- Source: A Pi defect that PiG fixed first (divergence D138)
- Symptom: A
session_compacthandler's passive custom message reaches the overflow retry (D138, Pi #9051). - Root cause: Pi #9051 (PR #9738 flush), Pi #5581 (PR #10739 event shape and run preparation).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/9051
- Removed when Pi fixes it: Pi flushes deferred custom messages before an overflow retry.
- Fix commits:
f19efb1eb7(2026-10-09) - Tests:
coding/extension/extension_event_test.go::TestBeforeAgentStartEventWireShape,coding/session_pi_bugs_test.go::TestIdleCustomMessageRunEmitsBeforeAgentStart,coding/session_pi_bugs_test.go::TestOverflowRetrySeesCustomMessagesPublishedBySessionCompact,internal/codingagent/interactive_custom_prompt_event_test.go::TestCustomSeedEmitsBeforeAgentStart - Release note: A
session_compacthandler's passive custom message reaches the overflow retry (D138, Pi #9051).
BF-f1a59f9
An OpenAI Responses event's output_index matches an output item as a key of Pi's Map does: a string…
- Source: PiG's own defect
- Symptom: An OpenAI Responses event's
output_indexmatches an output item as a key of Pi'sMapdoes: a string,null, a boolean or a missing index is its own key rather than a decode error or item 0, and an object never matches. - Root cause: The red claim above is re-checked on this base: the 9 new rows run against the parent commit's production code.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
5078f73100(2026-10-11),5932542c21(2026-10-11),82414338e7(2026-10-11),8eb1202528(2026-10-11),b6afa7be1a(2026-10-11),e1abc6c615(2026-10-11),f1a59f9161(2026-10-11) - Tests:
ai/anthropic_event_member_type_oracle_test.go::TestAnthropicEventMistypedMembersMatchPi,ai/mistral_chunk_member_type_oracle_test.go::TestMistralChunkMistypedMembersMatchPi,ai/openai_responses_member_type_oracle_test.go::TestOpenAIResponsesMistypedMembersMatchPi - Release note: An OpenAI Responses event's
output_indexmatches an output item as a key of Pi'sMapdoes: a string,null, a boolean or a missing index is its own key rather than a decode error or item 0, and an object never matches.
BF-f20cc08
A Node extension that calls pi.appendEntry many times in one handler no longer slows down with the size of…
- Source: PiG's own defect
- Symptom: A Node extension that calls
pi.appendEntrymany times in one handler no longer slows down with the size of the session: each append now costs the same, as in Pi. - Root cause: Upstream SessionManager.appendCustomEntry pushes the entry, sets byId and moves the leaf: O(1). Since every pi.appendEntry goes through the runtime's RuntimeSessionManager, which copied the pending list, rescanned the whole replicated log for confirmed entries and rebuilt the id index on every append, so k appends in one handler over an n-entry log cost O(k*(n+k)).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8d990c5d54(2026-10-07),31cde9f009(2026-10-09),7489735c3f(2026-10-09),f20cc081d5(2026-10-09) - Tests:
coding/cli/rpc_append_entry_visible_test.go::TestRPCNodeAppendEntryIsVisibleToTheSameHandlerAndEmitsEntryAppended,coding/extension/host/inproc/upstream_scoped_models_test.go::TestContextThinkingLevelReadsTheBoundRuntimeLive,coding/extension/host/subprocess/builder_node_entry_test.go::TestResolveNodeEntrypointImportsAnIndexMjsDirectoryAsJiti,test/extension-conformance/api_append_entry_test.go::TestConformance_ExtensionAPIAppendEntry,test/parity/runner/snapshot_test.go::TestSnapshotExtensionPathKeepsADirectoryExtensionsName,test/parity/scenarios/extensions-runtime/75-robust-notes-go.toml,test/parity/scenarios/extensions-runtime/75-robust-notes-node.toml,test/parity/scenarios/extensions-runtime/75-robust-notes-python.tomland 1 more in docs/bug-ledger.json - Release note: A Node extension that calls
pi.appendEntrymany times in one handler no longer slows down with the size of the session: each append now costs the same, as in Pi.
BF-f24a8ee
Fixed Markdown links not being clickable in Herdr: TERM_PROGRAM=herdr is now detected as supporting OSC 8…
- Source: PiG's own defect
- Symptom: Fixed Markdown links not being clickable in Herdr:
TERM_PROGRAM=herdris now detected as supporting OSC 8 hyperlinks, with images off. - Root cause: Ports packages/tui/src/terminal-image.ts TERM_PROGRAM=herdr branch (hyperlinks on, images off, before outer-terminal detection) and tui-alt-screen.ts resetTextSelection, called from renderSessionEntries (interactive-mode.ts, #9311).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f24a8eef3e(2026-10-07) - Tests:
internal/codingagent/interactive_transcript_selection_test.go::TestRenderSessionEntriesDropsFullscreenSelection,tui/terminal_image_upstream_test.go::TestUpstreamTerminalImageCapabilities,tui/tui_alt_screen_test.go::TestAltScreenResetTextSelectionDropsSelectionAndClickHistory - Release note: Fixed Markdown links not being clickable in Herdr:
TERM_PROGRAM=herdris now detected as supporting OSC 8 hyperlinks, with images off.
BF-f24a8ee-2
Fixed the fullscreen text selection and multi-click state surviving a session switch or other transcript…
- Source: PiG's own defect
- Symptom: Fixed the fullscreen text selection and multi-click state surviving a session switch or other transcript rebuild, which highlighted unrelated text in the new transcript.
- Root cause: Ports packages/tui/src/terminal-image.ts TERM_PROGRAM=herdr branch (hyperlinks on, images off, before outer-terminal detection) and tui-alt-screen.ts resetTextSelection, called from renderSessionEntries (interactive-mode.ts, #9311).
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f24a8eef3e(2026-10-07) - Tests:
internal/codingagent/interactive_transcript_selection_test.go::TestRenderSessionEntriesDropsFullscreenSelection,tui/terminal_image_upstream_test.go::TestUpstreamTerminalImageCapabilities,tui/tui_alt_screen_test.go::TestAltScreenResetTextSelectionDropsSelectionAndClickHistory - Release note: Fixed the fullscreen text selection and multi-click state surviving a session switch or other transcript rebuild, which highlighted unrelated text in the new transcript.
BF-f25348f
A turn_end or agent_before_settle handler in an extension process receives each…
- Source: PiG's own defect
- Symptom: A
turn_endoragent_before_settlehandler in an extension process receives eachcontext.contextEntries[].sourceEntrywith its members in the order the session wrote them, as in Pi, instead of sorted by name. - Root cause: Tests: inproc TestEmitBoundaryDecodesSourcesOnlyForHandlersThatReadGoValues (Go handlers get decoded sources in their own preview, JSON handlers none, an all-JSON dispatch decodes nothing); subprocess TestBoundaryWireEncodesEachSourceAsItsRecord (records on the wire, decoded and encoded after a Go handler, no sharing then); the surrogate test now goes through Deliver. Mutants killed: Deliver never called, Deliver for every handler, Encoded ignoring delivery, Deliver not marking delivery, the wire ignoring the records.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),34f7594aa5(2026-10-11),f25348fcf6(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/extension/host/inproc/boundary_test.go::TestEmitBoundaryDecodesSourcesOnlyForHandlersThatReadGoValues,coding/extension/host/subprocess/boundary_wire_test.go::TestBoundaryEventWireEncodesAsTheEvent,coding/extension/host/subprocess/boundary_wire_test.go::TestBoundaryWireCacheSharesOnlyAcrossUnchangedInputs,coding/extension/host/subprocess/boundary_wire_test.go::TestBoundaryWireEncodesEachSourceAsItsRecordand 15 more in docs/bug-ledger.json - Release note: A
turn_endoragent_before_settlehandler in an extension process receives eachcontext.contextEntries[].sourceEntrywith its members in the order the session wrote them, as in Pi, instead of sorted by name.
BF-f2790f9
A tui input listener or light/dark listener that is added while input is dispatched now sees that input…
- Source: PiG's own defect
- Symptom: A
tuiinput listener or light/dark listener that is added while input is dispatched now sees that input, and one that is removed before its turn no longer does, as Pi's listener sets iterate. - Root cause: Pi's TuiBase keeps inputListeners and terminalColorSchemeListeners in Sets that handleTerminalInput iterates with for...of (tui.ts:517-518, 1052-1062, 1174-1176): a listener added during dispatch sees the same input and one removed before its turn does not (probed on pi-tui TuiMainScreen: [a:x c:x]). RunInputListeners and ConsumeTerminalColorSchemeReport iterated a snapshot ([a:x b:x]). A liveSet iterates by position and tombstones removals made during an iteration. TestInputListenersIterateLiveAsPisSets fails on the snapshot, on an in-iteration delete, and on undropped tombstones.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f2790f9e0a(2026-10-09) - Tests:
tui/tui_input_pipeline_test.go::TestInputListenersIterateLiveAsPisSets - Release note: A
tuiinput listener or light/dark listener that is added while input is dispatched now sees that input, and one that is removed before its turn no longer does, as Pi's listener sets iterate.
BF-f288d69
MCP OAuth token and client registration responses keep empty lists, read expires_in like JavaScript's…
- Source: PiG's own defect
- Symptom: MCP OAuth token and client registration responses keep empty lists, read
expires_inlike JavaScript'sNumber(), treatnulltimestamps as absent, and keep registration members PiG does not model. - Root cause: The release commit's --move-unreleased moves them under [0.4.2].
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
f288d69087(2026-10-06) - Tests: None recorded.
- Release note: MCP OAuth token and client registration responses keep empty lists, read
expires_inlike JavaScript'sNumber(), treatnulltimestamps as absent, and keep registration members PiG does not model. A registration response keepstoken_endpoint_auth_methodand the other registered members when one member has an unexpected type, so the token request uses the authentication method the server registered, as in Pi.
BF-f288d69-2
On Windows, an npm self-update no longer fails when a virus scanner or indexer holds pig.exe open for a…
- Source: A Pi defect that PiG fixed first (divergence D99)
- Symptom: On Windows, an npm self-update no longer fails when a virus scanner or indexer holds
pig.exeopen for a moment. - Root cause: The release commit's --move-unreleased moves them under [0.4.2].
- User impact: An operation failed or was rejected
- Platform: Windows
- Fixed in: 0.5.0
- Pi issue: none recorded
- Removed when Pi fixes it: Pi retries the quarantine rename, or Windows stops refusing to rename a file that another process holds open.
- Commit that wrote the release note (the fix commit is not identified):
6a96295659(2026-10-06, #161),cbbaa8d2d7(2026-10-06),f288d69087(2026-10-06) - Tests:
coding/cli/self_update_windows_test.go::TestWindowsNpmSelfUpdateReplacesTheRunningInstallation,internal/codingagent/windows_self_update_windows_test.go::TestQuarantineWaitsOutAHandleHeldOnTheRunningImage - Release note: On Windows, an npm self-update no longer fails when a virus scanner or indexer holds
pig.exeopen for a moment. The update retries moving the loaded images for up to 10 seconds; any other error fails the update as before (D99).
BF-f288d69-3
ModelRuntime.Close cancels the remote model catalog's refreshes and waits for them, so none runs on after…
- Source: PiG's own defect
- Symptom:
ModelRuntime.Closecancels the remote model catalog's refreshes and waits for them, so none runs on afterClosereturns, and a refresh requested afterClosedoes not start. - Root cause: The release commit's --move-unreleased moves them under [0.4.2].
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
f288d69087(2026-10-06) - Tests:
coding/mcpext/connection_test.go::TestMCPConnectionsClosesAConnectionThatIsStillInitializingBeforeCloseReturns,coding/mcpext/connection_test.go::TestMCPConnectionsStopsWaitingToRetryAConnectionWhenClosed - Release note:
ModelRuntime.Closecancels the remote model catalog's refreshes and waits for them, so none runs on afterClosereturns, and a refresh requested afterClosedoes not start.
BF-f288d69-4
Crash attribution reads Go goroutine traces as well as JavaScript stacks, so a crash in the PiG process…
- Source: PiG's own defect
- Symptom: Crash attribution reads Go goroutine traces as well as JavaScript stacks, so a crash in the PiG process carries stack frames (D100).
- Root cause: The release commit's --move-unreleased moves them under [0.4.2].
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
f288d69087(2026-10-06) - Tests: None recorded.
- Release note: Crash attribution reads Go goroutine traces as well as JavaScript stacks, so a crash in the PiG process carries stack frames (D100).
BF-f29e906
OpenAI Completions, OpenAI Responses and Codex request bodies list their fields in the order Pi writes them…
- Source: PiG's own defect
- Symptom: OpenAI Completions, OpenAI Responses and Codex request bodies list their fields in the order Pi writes them, and sampling parameters from
models.jsonor the request replace a field in place and append new fields after the rest, as Pi'sObject.assigndoes, instead of sorting every key of the body. - Root cause: JSON.stringify writes params in the order Pi's builders create the keys, and Object.assign(params, samplingParams) keeps an existing key's position and appends a new one. PiG sorted every key of the body whenever sampling parameters or a non-default thinking budget field were set (marshal -> map[string]any -> marshal), and its struct order differed from Pi even without them (stream_options, store and prompt_cache_key landed late, tools early).
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f29e906cb3(2026-10-10) - Tests:
ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPi,ai/openai_request_key_order_oracle_test.go::TestOpenAICompletionsRequestKeysFollowPiAssignmentOrder,ai/openai_responses_request_key_order_oracle_test.go::TestOpenAIResponsesRequestKeysFollowPiAssignmentOrder - Release note: OpenAI Completions, OpenAI Responses and Codex request bodies list their fields in the order Pi writes them, and sampling parameters from
models.jsonor the request replace a field in place and append new fields after the rest, as Pi'sObject.assigndoes, instead of sorting every key of the body.
BF-f2a2b34
An unmatched tool result row in the /tree selector shows the tool's name, as in Pi ([bash]), instead of…
- Source: PiG's own defect
- Symptom: An unmatched tool result row in the /tree selector shows the tool's name, as in Pi (
[bash]), instead of[tool]. - Root cause: Label timestamps are toggled off before any save because a label saved now shows the wall clock.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
f2a2b3409b(2026-10-07) - Tests:
internal/codingagent/tree_row_format_test.go::TestFormatTreeRow_AllEntryTypes,internal/codingagent/tree_selector_oracle_test.go::TestTreeSelectorInputMatchesPi - Release note: An unmatched tool result row in the /tree selector shows the tool's name, as in Pi (
[bash]), instead of[tool].
BF-f2c8b02
After await ctx.reload(), a command's captured pi and ctx now throw Pi's stale message for every…
- Source: PiG's own defect
- Symptom: Members an SDK answers locally, such as
ctx.cwd,getFlag,getCommands,getThinkingLevel,getSystemPromptandisProjectTrusted, kept answering for the replaced extension instance. - Root cause: retireGeneration now sends the invalidate notify (Pi's stale message) to a replaced generation whose stop a running command defers, under retirementMu, so it precedes both the reply to the reload and the deferred stop's shutdown. An immediately stopped generation needs none (its runtime invalidates as its connection ends); sending it there deadlocked a retained Node reload whose invalidation unsubscribes its shared-bus listeners while the reload waits for the process to close. The Go SDK's Context.Reload waits for the notifies queued before its result, as AppendEntry does, because its main loop applies notifies after the read loop routed the result.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f2c8b02bd9(2026-10-10) - Tests:
coding/extension/host/subprocess/generation_retirement_test.go::TestReplacedGenerationCallsFailWithThePiStaleMessage,test/extension-conformance/sdkscenario_test.go::TestSDKScenarios,test/parity/scenarios/extensions-runtime/77-reload-stale-context.toml - Release note: After
await ctx.reload(), a command's capturedpiandctxnow throw Pi's stale message for every member, in every SDK (Node, Go, Python and Rust). Before, members an SDK answers locally, such asctx.cwd,getFlag,getCommands,getThinkingLevel,getSystemPromptandisProjectTrusted, kept answering for the replaced extension instance.
BF-f34b545
Durable: resolving harness settings no longer shares the default retry policy's max agent delay between…
- Source: PiG's own defect
- Symptom: Durable: resolving harness settings no longer shares the default retry policy's max agent delay between harnesses.
- Root cause: ResolveSettings now allocates its own.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f34b545a0c(2026-10-07) - Tests:
durable/harness/agent_defaults_test.go::TestDefaultPoliciesMatchPiAndResolveByCopy,durable/harness/agent_defaults_test.go::TestDefineExtensionReturnsTheExtension,durable/harness/harness_conversations_test.go::TestHarnessRootAndConversations,durable/harness/harness_tasks_scheduling_test.go::TestTaskClose,durable/harness_types_test.go::TestCompactionReasonValuesMatchPi - Release note: Durable: resolving harness settings no longer shares the default retry policy's max agent delay between harnesses.
BF-f3b4045
.gitignore, .ignore and .fdignore rules that skills, packages and extension entry discovery read now…
- Source: PiG's own defect
- Symptom:
.gitignore,.ignoreand.fdignorerules that skills, packages and extension entry discovery read now follow theignorepackage Pi uses: a pattern ending in a slash ignores directories only (a file namedbuildsurvivesbuild/), patterns match case-insensitively, a path inside an ignored directory stays ignored, a pattern with a slash before its end is anchored,[a-c]and[!a]bracket expressions work, and**/x,x/**andx/**/yfollow Pi's forms. - Root cause: TestProviderRequestHeadersMatchPi reads the URL path, method and headers each API module sends through the pinned pi-ai's options.fetch and through PiG's HTTP client (60 requests per API with session ids, caller header overrides, API-key forms and providers; client-library and transport headers excluded). The OpenAI-compatible requests now send accept, and directAPIProviderFor wires the Copilot dynamic headers (and bearer auth for anthropic) when the model's provider is github-copilot.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2b3403045b(2026-10-09),4333619e20(2026-10-09),5cec6423ef(2026-10-09),794d676720(2026-10-09),7cfdcb77b3(2026-10-09),ea72139272(2026-10-09),f3b4045e8a(2026-10-10) - Tests:
ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPi,ai/anthropic_oauth_test.go::TestAnthropicHistoryToolNames,ai/auth_file_bytes_oracle_test.go::TestAuthFileBytesMatchPi,ai/google_generative_ai_payload_oracle_test.go::TestGoogleGenerativeAIPayloadMatchesPi,ai/google_generative_ai_payload_oracle_test.go::TestGoogleVertexPayloadMatchesPi,ai/mistral_payload_oracle_test.go::TestMistralPayloadModeAndEffortMatchPi,ai/oauth_credential_members_test.go::TestOAuthBuildersWriteCredentialMembersInPiOrder,ai/openai_responses_payload_oracle_test.go::TestOpenAICodexResponsesPayloadMatchesPiand 7 more in docs/bug-ledger.json - Release note:
.gitignore,.ignoreand.fdignorerules that skills, packages and extension entry discovery read now follow theignorepackage Pi uses: a pattern ending in a slash ignores directories only (a file namedbuildsurvivesbuild/), patterns match case-insensitively, a path inside an ignored directory stays ignored, a pattern with a slash before its end is anchored,[a-c]and[!a]bracket expressions work, and**/x,x/**andx/**/yfollow Pi's forms. A leading space in a pattern is kept, as in Pi.
BF-f4d85f8
A large file written through a tool call no longer slows down as it streams: each streamed piece of the…
- Source: PiG's own defect
- Symptom: A large file written through a tool call no longer slows down as it streams: each streamed piece of the tool-call arguments is parsed on its own instead of re-parsing all the arguments so far after a provider opened the call with empty arguments, so the cost of the whole call grows with its size instead of with the square of its size.
- Root cause: changelog.d/perf-max.md: the user-visible quadratic large-write fix had no bullet. It states no time numbers because they were measured on a loaded shared host.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
f4d85f8a39(2026-10-10) - Tests:
ai/assistant_published_clone_test.go::TestCellObservationsCopyPublishedArgumentsUnread,ai/assistant_published_clone_test.go::TestObserveOfAnUnpublishedMessageStillChecksArgumentStrings,ai/streaming_json_resume_oracle_test.go::TestStreamingArgumentsParserMatchesPiAtEveryChunk - Release note: A large file written through a tool call no longer slows down as it streams: each streamed piece of the tool-call arguments is parsed on its own instead of re-parsing all the arguments so far after a provider opened the call with empty arguments, so the cost of the whole call grows with its size instead of with the square of its size.
BF-f5ecdc2
Settings and models.json compat objects keep the members PiG does not model, as Pi 1.2.0 does
- Source: PiG's own defect
- Symptom: Both were dropped on load (Pi 1.2.0
settings-manager.tsgetGlobalSettings and deepMergeSettings,provider-composer.tsmergeCompat,config-schemas.test.ts). - Root cause: Pi keeps every member of settings.json (getGlobalSettings returns them, deepMergeSettings merges them) and every member of a compat object (ProviderCompatSchema allows additional properties; mergeCompat spreads them into the composed model). Settings now holds the top-level members settingsWire does not decode, after migrateSettings, and writes them in its JSON form; mergeSettings deep-merges them. ai.OpenAICompat keeps unmodeled members in Extra through decode, encode and mergeCompat.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
21654635e7(2026-10-13),f5ecdc24d2(2026-10-13) - Tests:
coding/session_prompt_custom_run_upstream_test.go::TestUpstreamSessionPromptRunsStartedByACustomMessage,coding/session_tool_info_reaches_tools_test.go::TestGetAllToolsCarriesTheDefinitionsReachesTools,internal/codingagent/config_schemas_runtime_upstream_test.go::TestConfigSchemasAcceptCustomCompatibilitySettingsAndRejectMalformedKnownFields,internal/codingagent/config_schemas_runtime_upstream_test.go::TestConfigSchemasAcceptSchemaInKeybindingsJSONWithoutTreatingItAsAnAction,internal/codingagent/config_schemas_runtime_upstream_test.go::TestConfigSchemasAcceptSchemaInSettingsJSON,internal/codingagent/config_schemas_runtime_upstream_test.go::TestConfigSchemasValidateSchemaInModelsJSONAtRuntime,internal/codingagent/model_registry_merge_compat_fields_test.go::TestMergeCompatSpreadsEveryScalarCompatField,internal/codingagent/unmodeled_config_members_test.go::TestUnmodeledCompatMembersReachTheComposedModeland 12 more in docs/bug-ledger.json - Release note: Settings and
models.jsoncompat objects keep the members PiG does not model, as Pi 1.2.0 does.getGlobalSettings(),getSettings()and the settings JSON now include keys such as$schemaor an extension's own setting, project values merge over global ones member by member, and only the legacy keys a migration rewrote (queueMode,websockets) are dropped. A provider or modelcompatobject keeps keys such as a custom API'scustomOption, and the composed model carries them, so a custom API reads its own compat settings. Before, both were dropped on load (Pi 1.2.0settings-manager.tsgetGlobalSettings and deepMergeSettings,provider-composer.tsmergeCompat,config-schemas.test.ts).
BF-f6305d9
A new_session, fork, clone or switch_session in the same RPC batch as a compact aborts the…
- Source: PiG's own defect
- Symptom: A
new_session,fork,cloneorswitch_sessionin the same RPC batch as acompactaborts the compaction instead of waiting for it (D148). - Root cause: Not recorded.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f6305d9e5a(2026-10-10) - Tests:
coding/cli/rpc_replacement_order_test.go::TestRPCReplacementAbortsTheCompactionOfItsBatch - Release note: A
new_session,fork,cloneorswitch_sessionin the same RPC batch as acompactaborts the compaction instead of waiting for it (D148).
BF-f68aa40
A delta.Tracker no longer keeps memory alive that Pi's tracker releases
- Source: PiG's own defect
- Symptom: A
delta.Trackerno longer keeps memory alive that Pi's tracker releases. - Root cause:
- AssertValidOp never checked path safety: it accepted [-1], [1.5], [true], [null] and nested segments that Pi's assertPathArg rejects. - Its messages carried an "invalid delta operation: " prefix and wrong names ("t payload" for Pi's "t shape", "invalid ... path" for "path is empty"). - ApplyImmutable rejected a numeric segment on an object; Pi sets that key: applyImmutable({a:1}, [["s",[1],2]]) is {"1":2,"a":1}. - The public path_error test expected ["a","b","c"] where Pi reports the unresolvable container ["a","b"].
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
1f5a782c0a(2026-10-08),26f831bec5(2026-10-08),6ca5fde3a9(2026-10-08),d4e4a82ed9(2026-10-08),f2d7795fbb(2026-10-08),301e3c185f(2026-10-09),3e5b73b24a(2026-10-09),8c7bacff5b(2026-10-09),cb15f9d6b6(2026-10-09),f68aa400e5(2026-10-09) - Tests:
chord/delta/apply_immutable_upstream_test.go::TestCheckedImmutableOperationApplication,chord/delta/apply_immutable_upstream_test.go::TestImmutableApplicationComplexity,chord/delta/apply_numeric_key_test.go::TestApplyImmutableSetsANumericSegmentOnAnObjectAsAKey,chord/delta/apply_pi_oracle_test.go::TestApplyResolvesArraySegmentsInUpstreamOrder,chord/delta/apply_pi_oracle_test.go::TestTruncateInsideASurrogatePairKeepsTheLowHalf,chord/delta/compat_test.go::TestOpsDecodeKeepsObjectKeyOrder,chord/delta/compat_test.go::TestTheReleasedAPIKeepsWorking,chord/delta/delta_pi_differential_test.go::TestDeltaMatchesPiOnRandomCasesand 41 more in docs/bug-ledger.json - Release note: A
delta.Trackerno longer keeps memory alive that Pi's tracker releases. A settledChange, a draft handle kept after its change settled, and aPreparedno longer keep the draft, the base revision or theTrackeralive. An aborted change no longer stays registered with its tracker until the next adoption, and an open change the caller drops is reclaimed. Each case is one of Pi'sretention.test.tsscenarios.
BF-f68aa40-2
delta.CloneJSON, chord.CopyJSON and draft placements report a Go struct or pointer with Pi's message for…
- Source: PiG's own defect
- Symptom: The message named the Go kind.
- Root cause:
- AssertValidOp never checked path safety: it accepted [-1], [1.5], [true], [null] and nested segments that Pi's assertPathArg rejects. - Its messages carried an "invalid delta operation: " prefix and wrong names ("t payload" for Pi's "t shape", "invalid ... path" for "path is empty"). - ApplyImmutable rejected a numeric segment on an object; Pi sets that key: applyImmutable({a:1}, [["s",[1],2]]) is {"1":2,"a":1}. - The public path_error test expected ["a","b","c"] where Pi reports the unresolvable container ["a","b"].
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6f0c4625ee(2026-10-07),df51d7b7f8(2026-10-07),1804eaf46b(2026-10-08),1f5a782c0a(2026-10-08),26f831bec5(2026-10-08),2f86645d6b(2026-10-08),33c35623be(2026-10-08),5355335e3a(2026-10-08),6ca5fde3a9(2026-10-08),6cd85b3523(2026-10-08),9d34ce6e4f(2026-10-08),b7325c7314(2026-10-08),d722b24dfa(2026-10-08),d83a19a303(2026-10-08),e31e231285(2026-10-08),e6218c5dc4(2026-10-08),f2d7795fbb(2026-10-08),301e3c185f(2026-10-09),3e5b73b24a(2026-10-09),8c7bacff5b(2026-10-09),ac3759ebb7(2026-10-09),cb15f9d6b6(2026-10-09),f68aa400e5(2026-10-09) - Tests:
chord/delta/apply_immutable_upstream_test.go::TestCheckedImmutableOperationApplication,chord/delta/apply_immutable_upstream_test.go::TestImmutableApplicationComplexity,chord/delta/apply_numeric_key_test.go::TestApplyImmutableSetsANumericSegmentOnAnObjectAsAKey,chord/delta/apply_pi_oracle_test.go::TestApplyResolvesArraySegmentsInUpstreamOrder,chord/delta/apply_pi_oracle_test.go::TestTruncateInsideASurrogatePairKeepsTheLowHalf,chord/delta/compat_test.go::TestOpsDecodeKeepsObjectKeyOrder,chord/delta/compat_test.go::TestTheReleasedAPIKeepsWorking,chord/delta/delta_pi_differential_test.go::TestDeltaMatchesPiOnRandomCasesand 51 more in docs/bug-ledger.json - Release note:
delta.CloneJSON,chord.CopyJSONand draft placements report a Go struct or pointer with Pi's message for a value that is not a plain object (Value must contain strict JSON plain objects or arrays), and a Go func asValue contains a non-JSON function; expected strict JSON. Before, the message named the Go kind.
BF-f6e7d49
/tree opens and searches a 300 MB session faster than Pi: it reads the session's index and each message's…
- Source: PiG's own defect
- Symptom:
/treeopens and searches a 300 MB session faster than Pi: it reads the session's index and each message's search text instead of decoding every entry. - Root cause: Per lead rulings 01:42Z, 02:20Z item 3 and 04:25Z item 2 ((A)+(B)):
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
27d40bded4(2026-10-10),6e31cf8261(2026-10-10),747f9e050e(2026-10-10),7d65c14f4c(2026-10-10),8b65157e36(2026-10-10),c9ba8f3dbf(2026-10-10),deadeb8d16(2026-10-10),e097327828(2026-10-10),f6e7d49d7b(2026-10-10),f7821fbbf9(2026-10-10) - Tests:
bench/sessionscale/cmd/sessionscale/gate_test.go::TestResumeMemoryAndPromptCostFollowTheContext,bench/sessionscale/gen_test.go::TestGenerateWritesTheScaledOwnerShape,coding/cli/rpc_mode_test.go::TestRPCTreeSerializesUpstreamShape,internal/codingagent/session_lazy_test.go::TestHandleReadsMatchDecodedReads,internal/codingagent/session_lazy_test.go::TestLazyEntriesKeepTheirTimestamps,internal/codingagent/session_lazy_test.go::TestLazyEntryOfAChangedRecordIsAnError,internal/codingagent/session_store_norace_test.go::TestIndexKeepsNoLargeSearchText,internal/codingagent/session_store_norace_test.go::TestResumedSessionRetainsContextNotHistoryand 22 more in docs/bug-ledger.json - Release note:
/treeopens and searches a 300 MB session faster than Pi: it reads the session's index and each message's search text instead of decoding every entry. RPCget_entriesandget_tree, extension session reads and branch summaries read message records from the session file only when they need them, so these whole-history reads no longer decode the history.
BF-f6e7d49-2
A session without a file (--no-session) keeps the records of entries behind its latest compaction…
- Source: PiG's own defect
- Symptom: A session without a file (
--no-session) keeps the records of entries behind its latest compaction compressed instead of decoded. - Root cause: Per lead rulings 01:42Z, 02:20Z item 3 and 04:25Z item 2 ((A)+(B)):
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
7d65c14f4c(2026-10-10),8b65157e36(2026-10-10),deadeb8d16(2026-10-10),e097327828(2026-10-10),f6e7d49d7b(2026-10-10) - Tests:
bench/sessionscale/cmd/sessionscale/gate_test.go::TestResumeMemoryAndPromptCostFollowTheContext,coding/cli/rpc_mode_test.go::TestRPCTreeSerializesUpstreamShape,internal/codingagent/session_lazy_test.go::TestHandleReadsMatchDecodedReads,internal/codingagent/session_lazy_test.go::TestLazyEntriesKeepTheirTimestamps,internal/codingagent/session_lazy_test.go::TestLazyEntryOfAChangedRecordIsAnError,internal/codingagent/session_store_norace_test.go::TestResumedSessionRetainsContextNotHistory,internal/codingagent/session_store_norace_test.go::TestSessionWithoutFileRetainsCompressedHistory,internal/codingagent/session_store_norace_test.go::TestTurnCostIndependentOfHistoryBehindCompactionand 14 more in docs/bug-ledger.json - Release note: A session without a file (
--no-session) keeps the records of entries behind its latest compaction compressed instead of decoded.
BF-f6e7d49-3
RPC get_entries, RPC get_tree and extension session reads now fail with an error naming the entry when…
- Source: PiG's own defect
- Symptom: RPC
get_entries, RPCget_treeand extension session reads now fail with an error naming the entry when the session file was changed under PiG, instead of returning the entry's base members alone. - Root cause: Per lead rulings 01:42Z, 02:20Z item 3 and 04:25Z item 2 ((A)+(B)):
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8b65157e36(2026-10-10),c9ba8f3dbf(2026-10-10),deadeb8d16(2026-10-10),e097327828(2026-10-10),f6e7d49d7b(2026-10-10) - Tests:
bench/sessionscale/cmd/sessionscale/gate_test.go::TestResumeMemoryAndPromptCostFollowTheContext,bench/sessionscale/gen_test.go::TestGenerateWritesTheScaledOwnerShape,coding/cli/rpc_mode_test.go::TestRPCTreeSerializesUpstreamShape,internal/codingagent/session_lazy_test.go::TestHandleReadsMatchDecodedReads,internal/codingagent/session_lazy_test.go::TestLazyEntriesKeepTheirTimestamps,internal/codingagent/session_lazy_test.go::TestLazyEntryOfAChangedRecordIsAnError,internal/codingagent/session_store_norace_test.go::TestResumedSessionRetainsContextNotHistory,internal/codingagent/session_store_norace_test.go::TestSessionWithoutFileRetainsCompressedHistoryand 14 more in docs/bug-ledger.json - Release note: RPC
get_entries, RPCget_treeand extension session reads now fail with an error naming the entry when the session file was changed under PiG, instead of returning the entry's base members alone.
BF-f73c00c
Replaying a thinking block that holds only U+FEFF drops it and one that holds only U+0085 keeps it, as…
- Source: PiG's own defect
- Symptom: Replaying a thinking block that holds only U+FEFF drops it and one that holds only U+0085 keeps it, as JavaScript trim() decides in Pi.
- Root cause: Pi's transformMessages skips a thinking block whose text is empty after trim() (transform-messages.ts:111). Go's TrimSpace strips U+0085 and keeps U+FEFF, so a block of only U+FEFF was kept and replayed as text or thinking, and a block of only U+0085 was dropped, where Pi does the opposite. normalizeAssistantReplayBlocks now uses jsstring.Trim.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f73c00c391(2026-10-11) - Tests:
agent/agent_class_pi_oracle_test.go::TestAgentClassMatchesPi,agent/agent_loop_pi_oracle_test.go::TestAgentLoopMatchesPi,agent/transform_thinking_oracle_test.go::TestNormalizeAssistantReplayThinkingTrimMatchesPi - Release note: Replaying a thinking block that holds only U+FEFF drops it and one that holds only U+0085 keeps it, as JavaScript trim() decides in Pi.
BF-f78b951
fix(codingagent): cache the Markdown transformer list per theme and runner (TU-T6)
- Source: PiG's own defect
- Symptom: fix(codingagent): cache the Markdown transformer list per theme and runner (TU-T6)
- Root cause: The assistant and user Markdown transforms apply the cached Mermaid and extension transformer list directly instead of building the list, a Mermaid closure and a transform closure per render. Drops the updateAssistantMessageBlockFromOwned alias (tui-ALIAS).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
f78b951554(2026-10-13) - Tests: None recorded.
BF-f7b245a
A frontend member's main region now matches the transcript after an extension's custom entry is placed before…
- Source: PiG's own defect
- Symptom: A frontend member's main region now matches the transcript after an extension's custom entry is placed before the streaming message and a later child of the transcript changes in the same frame.
- Root cause: pig piglet show lists the slot, the schema gains slots and removeSpec.slots, and a Binary's embedded Piglet carries no slots because the member is compiled in. The Binary record keeps the member's tree digest as a content-addressed frontend input.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f5190b9c48(2026-10-04),35ad0aaea9(2026-10-05),613c997d21(2026-10-05),69ee29f0d4(2026-10-05),70ca2f3ce2(2026-10-05),9846de83dc(2026-10-05),c027bb425f(2026-10-05),a03488a003(2026-10-06),f7b245a8c1(2026-10-08) - Tests:
coding/piglet/frontend_dir_test.go::TestFrontendDirResolvesInsideThePigletDirectory,coding/piglet/frontend_slot_test.go::TestFrontendSlotInheritsReplacesAndRemoves,coding/piglet/piglet_test.go::TestParseBuildSpecRejectsInvalidDefaults,coding/piglet/schema_test.go::TestAC1SchemaParserAgreement,coding/pigletbuild/fuse_modules_test.go::TestMergeFusedModulesTakesTheHighestMemberVersionAndAnchorsLocalReplacements,coding/pigletbuild/native_build_test.go::TestOverlayFuseImportsFactoryPackageWithoutWritingGoMod,coding/pigletbuild/native_build_test.go::TestOverlayFuseRegistersFrontendMember,coding/pigletbuild/plan_test.go::TestValidateRejectsNonFusedExtensionWhenRequiredand 98 more in docs/bug-ledger.json - Release note: A frontend member's main region now matches the transcript after an extension's custom entry is placed before the streaming message and a later child of the transcript changes in the same frame.
Container.InsertBeforedid not report the children it kept, so the frame spliced a stale range: the session kept the removed entry and missed the inserted one. An insert alone also walked the whole transcript instead of the inserted entry.
BF-f7c4b01
pig extension upgrade no longer changes, breaks or rejects a Go extension that already builds against the…
- Source: PiG's own defect
- Symptom:
pig extension upgradeno longer changes, breaks or rejects a Go extension that already builds against the current SDK. - Root cause: fmt.Println(ctx.ConfigHome()) compiles against the current SDK: the value and the error spread into ...any, so no compile error marks it and the error is printed instead of handled. Find a getter call that is the only argument of a variadic call (or print/println) syntactically, in files without compile errors too, and rewrite it as the other argument positions: hoist it and return the error, or list a ConfigHome whose function cannot return it. A non-variadic call whose parameters take the value and the error is the new shape and stays. The fixture test now also fails when a file with a golden source is not rewritten.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
63bb0b7eac(2026-10-12),974050757c(2026-10-12),d305b76bda(2026-10-12),f7c4b01ccc(2026-10-12) - Tests:
coding/cli/extension_upgrade_test.go::TestUpgraderRestoresEveryFileWhenTheRebuildFails,coding/cli/extension_upgrade_test.go::TestUpgraderRestoresWrittenFilesWhenAWriteFails,coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestGoSDKPathOverrideKeepsCurrentStagedSDK,coding/extension/host/runtimecell/go_sdk_override_test.go::TestRustSDKPathOverrideIgnoresRetiredStagedSDK,coding/extension/host/subprocess/builder_test.go::TestGoNeedsStagedSDK,coding/extension/host/subprocess/builder_test.go::TestRustNeedsStagedSDK,extensions/sdk/upgrade/fixtures_test.go::TestFixturesUpgradeAndBuildand 6 more in docs/bug-ledger.json - Release note:
pig extension upgradeno longer changes, breaks or rejects a Go extension that already builds against the current SDK. A getter passed straight into a variadic call, such asfmt.Println(ctx.GetActiveTools()), is current code when the getter returned its error in the SDK the extension'sgo.modrequires, so it is left as it is. The upgrade now rewrites onlyConfigHomecalls of this kind, in extensions that require an SDK older than 0.5.0, and lists such a call rather than discard its error. A call whose parameter for the error has the typeerrorstays. Rewriting one of these calls no longer also rewritesContextUsage.TokensandPercentuses that were already correct. A getter in aselectcase is listed instead of making the upgrade fail withthe rewrite does not parse.
BF-f7d701e
Tool argument validation asserts the format keyword for the formats TypeBox checks (date, time, date-time…
- Source: PiG's own defect
- Symptom: A value that violated a format passed validation.
- Root cause: Pig compiled tool schemas without format assertions, so a value that breaks 'format' (email, uri, date-time...) passed where TypeBox's Check fails with 'must match format'. validate_formats.go ports typebox 1.3.27 build/format (17 of its 21 formats: date, time, date-time, duration, email, ipv4, ipv6, iri, iri-reference, json-pointer, json-pointer-uri-fragment, relative-json-pointer, uri, uri-reference, uri-template, url, uuid; the regexp sources are taken from the .mjs files; hostname, idn-hostname, idn-email and regex keep the schema library's checks) and registers them with AssertFormat.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e8cc072521(2026-10-08),093cf5c781(2026-10-09),6f4a82fd86(2026-10-09),d1b3ad0151(2026-10-09),d1cbde3321(2026-10-09),d490a8b161(2026-10-09),f7d701e67d(2026-10-09) - Tests:
ai/error_body_oracle_test.go::TestErrorBodyHelpersMatchPiOnSeededProbes,ai/radius_config_oracle_test.go::TestRadiusConfigMatchesPiOnSeededProbes,ai/service_urls_oracle_test.go::TestProviderServiceURLsMatchPiOnSeededProbes,ai/validate_formats_oracle_test.go::TestTypeboxFormatsMatchPiOnSeededStrings,ai/validate_tool_args_oracle_test.go::TestValidateToolArgumentsMatchesPiOnSeededSchemas,chord/delta/own_key_order_test.go::TestDiffRevisionsVisitsArrayIndexKeysFirstInNumericOrder,internal/chord/chordjson/own_key_order_test.go::TestCopyFollowsPisKeyOrderAndMessages,internal/evals/report_oracle_test.go::TestEvalReportMatchesPiOnSeededDesigns - Release note: Tool argument validation asserts the
formatkeyword for the formats TypeBox checks (date, time, date-time, duration, email, ipv4, ipv6, iri, iri-reference, json-pointer, json-pointer-uri-fragment, relative-json-pointer, uri, uri-reference, uri-template, url, uuid) with TypeBox's own rules. Before, a value that violated a format passed validation.
BF-f8673e6
Tool argument conversion for TypeBox schemas lower-cases with JavaScript rules (an İ in UNDEFİNED no…
- Source: PiG's own defect
- Symptom: Tool argument conversion for TypeBox schemas lower-cases with JavaScript rules (an
İinUNDEFİNEDno longer converts to null) and matches RecordpatternPropertieskeys as the anchored expression^pattern$, as TypeBox does. - Root cause: The generator now puts ~kind Object/Array/Tuple/Union on its nodes and adds a fixed matrix of kinds (Boolean, Number, Integer, String, Null, Literal, Array, Union, Tuple, Record) against 40+ padded, cased and typed argument spellings. Two differences found (typebox 1.3.27 build/value/convert): coerceArgumentPrimitive lower-cased with Go's ToLower, which maps U+0130 to 'i', so 'UNDEFİNED' converted to null where TypeBox's toLowerCase keeps 'i̇' (now jsstring.ToLower); and FromRecord tests each pattern as new RegExp('^'+key+'$') while Pig compiled it unanchored (mutant killed: pattern anchor in the converter). Equivalent mutants: ToLower->strings.ToLower on the 'true/false' comparisons (no…
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6f4a82fd86(2026-10-09),a1fa6d1a33(2026-10-09),d490a8b161(2026-10-09),f7d701e67d(2026-10-09),f8673e6c49(2026-10-09) - Tests:
ai/node_http_proxy_oracle_test.go::TestResolveHTTPProxyURLForTargetMatchesPiOracle,ai/overflow_oracle_test.go::TestContextOverflowMatchesPiOnSeededMessages,ai/validate_formats_oracle_test.go::TestTypeboxFormatsMatchPiOnSeededStrings,ai/validate_tool_args_oracle_test.go::TestValidateToolArgumentsMatchesPiOnSeededSchemas,internal/evals/plan_oracle_test.go::TestPlanMatchesPiOnSeededProbes - Release note: Tool argument conversion for TypeBox schemas lower-cases with JavaScript rules (an
İinUNDEFİNEDno longer converts to null) and matches RecordpatternPropertieskeys as the anchored expression^pattern$, as TypeBox does.
BF-f90fedc
fix(cli): satisfy errcheck and gocritic in the validate-only change
- Source: PiG's own defect
- Symptom: fix(cli): satisfy errcheck and gocritic in the validate-only change
- Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
f90fedc1f1(2026-10-22) - Tests: None recorded.
BF-f9648c6
On Windows, stopping a terminal reader whose input is a pipe no longer hangs
- Source: PiG's own defect
- Symptom: On Windows, stopping a terminal reader whose input is a pipe no longer hangs.
- Root cause: TestInteractiveShutdownLeavesTerminalFileForNextReader timed out after 30m in Windows native-3 (run 40075386948, job 120845797941; twice more on 2026-10-12 and 2026-10-24). The dump has the test goroutine in interactiveTerminalReader.pause waiting for the reader session, and the session in tui.ReadInputChunk -> ReadFile on the pipe the test uses as its terminal.
- User impact: Crash, hang or runaway process
- Platform: Windows
- Fixed in: Unreleased
- Fix commits:
0869584873(2026-10-24),f9648c6fd5(2026-10-24) - Tests:
internal/codingagent/keystroke_hop_test.go::TestKeystrokeReachesTheOwnerInOneHandOff,internal/codingagent/shutdown_input_owner_test.go::TestInteractiveShutdownLeavesTerminalFileForNextReader,tui/terminal_native_windows_test.go::TestWindowsInputWaiterTimesOutSignalsAndCancels,tui/terminal_pipe_windows_test.go::TestReadInputStreamDeliversAPipeToItsEnd,tui/terminal_pipe_windows_test.go::TestReadInputStreamLeavesAPipeForTheNextReader - Release note: On Windows, stopping a terminal reader whose input is a pipe no longer hangs. The reader waited on the input handle for input, but a pipe handle reads as ready whether or not input is queued, so a reader over a pipe went into a read that cancellation could not end, and
Stop, shutdown and an external-editor handoff waited on it for good (seen as a 30 minuteinternal/codingagenttest timeout in Windows CI). A pipe is now waited on in a zero-byte read, as Node's libuv does, which takes no input and which cancelling the reader ends, so a cancelled reader returns at once and leaves later input to the next reader. A console, whichpigitself reads, is waited on as before.
BF-f98d129
Clearing the queued messages (RPC clear_queue, the SDK, the interactive restore-to-editor key) no longer…
- Source: A Pi defect that PiG fixed first (divergence D135)
- Symptom: Clearing the queued messages (RPC
clear_queue, the SDK, the interactive restore-to-editor key) no longer destroys custom messages that an extension queued withsendMessage({ deliverAs }). - Root cause: Pi #9886, #10287, #10162. #9410 does not reproduce (test pins Pi main's behavior).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/9886
- Removed when Pi fixes it: Pi's
clearQueuestops destroying custom messages it does not return. - Fix commits:
5731351f4e(2026-10-09),f98d129beb(2026-10-09) - Tests:
agent/image_limit_test.go::TestLimitImagesPerRequestKeepsTheNewestImages,agent/queue_clear_user_test.go::TestClearUserMessagesKeepsCustomMessagesInOrder,coding/session_pi_bugs_test.go::TestAbortInOverThresholdSessionDoesNotCompactBeforeSettling,coding/session_pi_bugs_test.go::TestClearQueueKeepsExtensionCustomMessages,coding/session_pi_bugs_test.go::TestContextUsageSurvivesAnOmittedFailedRetryAttempt,coding/session_pi_bugs_test.go::TestPromptSendsAtMostTheModelsImagesPerRequest,internal/codingagent/compaction/compaction_oracle_test.go::TestCompactionMatchesPi,internal/codingagent/compaction/context_edit_retry_anchor_test.go::TestEstimateDropsUsageAnchorWhenTheAnchorOrEarlierInputIsEditedand 3 more in docs/bug-ledger.json - Release note: Clearing the queued messages (RPC
clear_queue, the SDK, the interactive restore-to-editor key) no longer destroys custom messages that an extension queued withsendMessage({ deliverAs }). They stay queued and run with the active run (D135, Pi #9886).
BF-f98d129-2
Omitting a failed retry attempt no longer makes the context usage jump to an estimate of the whole history
- Source: A Pi defect that PiG fixed first (divergence D136)
- Symptom: Omitting a failed retry attempt no longer makes the context usage jump to an estimate of the whole history.
- Root cause: Pi #9886, #10287, #10162. #9410 does not reproduce (test pins Pi main's behavior).
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10287
- Removed when Pi fixes it: Pi keeps the usage anchor across an edit that only touches later entries.
- Fix commits:
5731351f4e(2026-10-09),f98d129beb(2026-10-09) - Tests:
agent/image_limit_test.go::TestLimitImagesPerRequestKeepsTheNewestImages,agent/queue_clear_user_test.go::TestClearUserMessagesKeepsCustomMessagesInOrder,coding/session_pi_bugs_test.go::TestAbortInOverThresholdSessionDoesNotCompactBeforeSettling,coding/session_pi_bugs_test.go::TestClearQueueKeepsExtensionCustomMessages,coding/session_pi_bugs_test.go::TestContextUsageSurvivesAnOmittedFailedRetryAttempt,coding/session_pi_bugs_test.go::TestPromptSendsAtMostTheModelsImagesPerRequest,internal/codingagent/compaction/compaction_oracle_test.go::TestCompactionMatchesPi,internal/codingagent/compaction/context_edit_retry_anchor_test.go::TestEstimateDropsUsageAnchorWhenTheAnchorOrEarlierInputIsEditedand 2 more in docs/bug-ledger.json - Release note: Omitting a failed retry attempt no longer makes the context usage jump to an estimate of the whole history. A context edit that only touches entries after the last usage keeps that usage (D136, Pi #10287).
BF-f98d129-3
A request carries at most the model's inputLimits.images.maxPerRequest images
- Source: A Pi defect that PiG fixed first (divergence D137)
- Symptom: A request carries at most the model's
inputLimits.images.maxPerRequestimages. - Root cause: Pi #9886, #10287, #10162. #9410 does not reproduce (test pins Pi main's behavior).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Pi issue: https://github.com/earendil-works/pi/issues/10162
- Removed when Pi fixes it: Pi enforces
inputLimits.images.maxPerRequeston provider requests. - Fix commits:
5731351f4e(2026-10-09),f98d129beb(2026-10-09) - Tests:
agent/image_limit_test.go::TestLimitImagesPerRequestKeepsTheNewestImages,agent/queue_clear_user_test.go::TestClearUserMessagesKeepsCustomMessagesInOrder,coding/session_pi_bugs_test.go::TestAbortInOverThresholdSessionDoesNotCompactBeforeSettling,coding/session_pi_bugs_test.go::TestClearQueueKeepsExtensionCustomMessages,coding/session_pi_bugs_test.go::TestContextUsageSurvivesAnOmittedFailedRetryAttempt,coding/session_pi_bugs_test.go::TestPromptSendsAtMostTheModelsImagesPerRequest,internal/codingagent/compaction/compaction_oracle_test.go::TestCompactionMatchesPi,internal/codingagent/compaction/context_edit_retry_anchor_test.go::TestEstimateDropsUsageAnchorWhenTheAnchorOrEarlierInputIsEditedand 2 more in docs/bug-ledger.json - Release note: A request carries at most the model's
inputLimits.images.maxPerRequestimages. Older images become a text placeholder in the request and stay in the session (D137, Pi #10162).
BF-f992050
fix(ai,durable): keep a value that is not JSON an error when a message or a nested result is encoded
- Source: PiG's own defect
- Symptom: fix(ai,durable): keep a value that is not JSON an error when a message or a nested result is encoded
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
f992050b6d(2026-10-24) - Tests: None recorded.
BF-fa3da97
A wake the scheduler took at a change is still delivered to OnWake when the Session fails and seals before…
- Source: PiG's own defect
- Symptom: A wake the scheduler took at a change is still delivered to
OnWakewhen the Session fails and seals before the goroutine publishing it runs, as Pi's microtask ran first; a failing recovery pass no longer loses the host's active wake. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Lost or altered data
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A wake the scheduler took at a change is still delivered to
OnWakewhen the Session fails and seals before the goroutine publishing it runs, as Pi's microtask ran first; a failing recovery pass no longer loses the host's active wake.
BF-fa3da97-10
A model reply with many tool calls no longer slows down as calls finish: finished calls are not parsed again…
- Source: PiG's own defect
- Symptom: A model reply with many tool calls no longer slows down as calls finish: finished calls are not parsed again on every later stream event.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A model reply with many tool calls no longer slows down as calls finish: finished calls are not parsed again on every later stream event.
BF-fa3da97-11
Durable task-graph and view watches no longer send or rebuild the whole graph or view on each commit: a…
- Source: PiG's own defect
- Symptom: Durable task-graph and view watches no longer send or rebuild the whole graph or view on each commit: a commit costs the same at any number of live tasks or entries.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Durable task-graph and view watches no longer send or rebuild the whole graph or view on each commit: a commit costs the same at any number of live tasks or entries.
BF-fa3da97-12
MCP: a server's tools, the tools it stopped offering, and the resource tools now reach the session as one…
- Source: PiG's own defect
- Symptom: MCP: a server's tools, the tools it stopped offering, and the resource tools now reach the session as one change, as Pi registers them in one synchronous turn.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: MCP: a server's tools, the tools it stopped offering, and the resource tools now reach the session as one change, as Pi registers them in one synchronous turn. A prompt that started while a server was connecting could see only some of its tools.
BF-fa3da97-13
On the Cloudflare Durable host, an AsyncLocalStorage value set around a call stays visible in the work that…
- Source: PiG's own defect
- Symptom: On the Cloudflare Durable host, an
AsyncLocalStoragevalue set around a call stays visible in the work that call queues (turns, tool effects, timers and commit listeners), as in Pi. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: On the Cloudflare Durable host, an
AsyncLocalStoragevalue set around a call stays visible in the work that call queues (turns, tool effects, timers and commit listeners), as in Pi.
BF-fa3da97-14
A Durable Session whose commit was cancelled while it waited for the Session line now closes instead of…
- Source: PiG's own defect
- Symptom: A Durable Session whose commit was cancelled while it waited for the Session line now closes instead of hanging in
Close. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A Durable Session whose commit was cancelled while it waited for the Session line now closes instead of hanging in
Close.
BF-fa3da97-15
An extension that registers many tools after load (pi.registerTool) no longer slows down with each…
- Source: PiG's own defect
- Symptom: An extension that registers many tools after load (
pi.registerTool) no longer slows down with each registration. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: An extension that registers many tools after load (
pi.registerTool) no longer slows down with each registration. The registration reply and state pushes no longer carry the whole tool catalog, and the Session no longer re-reads every tool on each registration.getAllToolsandgetActiveToolsreturn the same results as before.
BF-fa3da97-16
A context event with several extension handlers is encoded once per request instead of once per handler…
- Source: PiG's own defect
- Symptom: A
contextevent with several extension handlers is encoded once per request instead of once per handler, and a handler that leaves the conversation unchanged no longer sends it back. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A
contextevent with several extension handlers is encoded once per request instead of once per handler, and a handler that leaves the conversation unchanged no longer sends it back.
BF-fa3da97-17
Rust SDK: a context handler that returns a new list equal to the one it received now counts as a…
- Source: PiG's own defect
- Symptom: Rust SDK: a
contexthandler that returns a new list equal to the one it received now counts as a replacement, and an in-place edit marked withpig_sdk::context_unchanged()keeps system messages in place, as in Pi and the other SDKs. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Rust SDK: a
contexthandler that returns a new list equal to the one it received now counts as a replacement, and an in-place edit marked withpig_sdk::context_unchanged()keeps system messages in place, as in Pi and the other SDKs. ReturnNonefor an unchanged conversation.
BF-fa3da97-18
A Durable core session with many live tasks recomputes its next wake without walking every live task on each…
- Source: PiG's own defect
- Symptom: A Durable core session with many live tasks recomputes its next wake without walking every live task on each commit: ending a chain of 20,000 held tasks takes under a second instead of about a minute.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A Durable core session with many live tasks recomputes its next wake without walking every live task on each commit: ending a chain of 20,000 held tasks takes under a second instead of about a minute.
BF-fa3da97-19
Idle and typing frames in a long interactive session no longer redraw from the header down: a frame costs the…
- Source: PiG's own defect
- Symptom: Idle and typing frames in a long interactive session no longer redraw from the header down: a frame costs the same at any transcript length.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Idle and typing frames in a long interactive session no longer redraw from the header down: a frame costs the same at any transcript length. Extension headers and footers still update every frame.
BF-fa3da97-2
env/daemon: listFiles with timeoutMs 0 truncates at once on Windows too; the deadline check no longer…
- Source: PiG's own defect
- Symptom:
env/daemon:listFileswithtimeoutMs0 truncates at once on Windows too; the deadline check no longer misses on the platform's coarse clock. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: Windows
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note:
env/daemon:listFileswithtimeoutMs0 truncates at once on Windows too; the deadline check no longer misses on the platform's coarse clock.
BF-fa3da97-20
Markdown with many inline $ signs renders in linear time, as in Pi
- Source: PiG's own defect
- Symptom: Markdown with many inline
$signs renders in linear time, as in Pi. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Markdown with many inline
$signs renders in linear time, as in Pi.
BF-fa3da97-21
Extension SDKs: reading the session branch, an entry, a label or the leaf entry right after a new entry is…
- Source: PiG's own defect
- Symptom: Extension SDKs: reading the session branch, an entry, a label or the leaf entry right after a new entry is appended no longer costs time proportional to the session length, in the Go, Node, Python and Rust SDKs.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Extension SDKs: reading the session branch, an entry, a label or the leaf entry right after a new entry is appended no longer costs time proportional to the session length, in the Go, Node, Python and Rust SDKs.
BF-fa3da97-22
Choosing a bundled model no longer builds every model of its provider on each request, and providers in…
- Source: PiG's own defect
- Symptom: Choosing a bundled model no longer builds every model of its provider on each request, and providers in
models.jsonwith many custom models no longer slow down quadratically. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Choosing a bundled model no longer builds every model of its provider on each request, and providers in
models.jsonwith many custom models no longer slow down quadratically. Branch summaries of long sessions are prepared in linear time.
BF-fa3da97-23
chord/host: a host no longer sends on another host's connection while delivering notifications, and a…
- Source: PiG's own defect
- Symptom:
chord/host: a host no longer sends on another host's connection while delivering notifications, and a reload that unwinds frees its slot and ends its operation together, so a following reload is not refused as already running. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note:
chord/host: a host no longer sends on another host's connection while delivering notifications, and a reload that unwinds frees its slot and ends its operation together, so a following reload is not refused as already running.
BF-fa3da97-24
A Durable code mode tool call made just before the script launches another call or ends has started before…
- Source: PiG's own defect
- Symptom: A Durable code mode tool call made just before the script launches another call or ends has started before the script goes on, as in Pi: a later call no longer overtakes it, and the end of the script no longer aborts it before its tool ran.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A Durable code mode tool call made just before the script launches another call or ends has started before the script goes on, as in Pi: a later call no longer overtakes it, and the end of the script no longer aborts it before its tool ran.
BF-fa3da97-25
Fixed pig piglet build failing on a Piglet that carries a single-file extension (a lone .ts or .js…
- Source: PiG's own defect
- Symptom: Fixed
pig piglet buildfailing on a Piglet that carries a single-file extension (a lone.tsor.jsfile): locking it read<file>/go.mod, which fails with ENOTDIR rather than ENOENT, so the build stopped with "local Go replacements: not a directory". - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Fixed
pig piglet buildfailing on a Piglet that carries a single-file extension (a lone.tsor.jsfile): locking it read<file>/go.mod, which fails with ENOTDIR rather than ENOENT, so the build stopped with "local Go replacements: not a directory". A file extension has no Go replacements and now locks like a directory extension without ago.mod.
BF-fa3da97-26
A provider stream could stop for good, with the turn waiting for events that never came, when the goroutine…
- Source: PiG's own defect
- Symptom: A provider stream could stop for good, with the turn waiting for events that never came, when the goroutine that reads the stream and the goroutine that runs the provider's response were scheduled against each other as the stream started.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: Windows, macOS, Linux
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A provider stream could stop for good, with the turn waiting for events that never came, when the goroutine that reads the stream and the goroutine that runs the provider's response were scheduled against each other as the stream started. The reader's goroutine could pick up a reaction loop that belonged to the response, run the reader's code beside a reaction that had not returned, and leave the stream's executor marked busy with nothing running. A goroutine now continues only a reaction loop its own turn holds, so no code runs beside a reaction and every stream ends. It stalled once in CI on Windows (
TestGoogleProviderStreamMatchesPi); the same stall reproduces on macOS and Linux when the executor's lock hand-offs are delayed.
BF-fa3da97-27
PiG no longer hangs for 30 seconds at exit when two extensions share a Node process and one is still waiting…
- Source: PiG's own defect
- Symptom: PiG no longer hangs for 30 seconds at exit when two extensions share a Node process and one is still waiting on a host call (seen with a global project-trust extension and a project extension).
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: PiG no longer hangs for 30 seconds at exit when two extensions share a Node process and one is still waiting on a host call (seen with a global project-trust extension and a project extension).
BF-fa3da97-28
A Node extension whose factory failed, because it has no default export or throws while initializing, could…
- Source: PiG's own defect
- Symptom: A Node extension whose factory failed, because it has no default export or throws while initializing, could be reported as
connect:process_exited ... exit status 1instead of Pi's "Extension does not export a valid factory function: <path>" or "Failed to load extension: <message>". - Root cause: A Node extension whose factory failed, because it has no default export or throws while initializing, could be reported as
connect:process_exited ... exit status 1instead of Pi's "Extension does not export a valid factory function: <path>" or "Failed to load extension: <message>". - User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A Node extension whose factory failed, because it has no default export or throws while initializing, could be reported as
connect:process_exited ... exit status 1instead of Pi's "Extension does not export a valid factory function: <path>" or "Failed to load extension: <message>". The extension's runtime reports that error over its socket and exits; on a loaded machine the host looked after the exit and chose between the connection and the exit by whichever it noticed first, which failed about 1 run in 20 of the upstream extension-discovery tests. The host now accepts the connection the extension made before it exited, in both a packed cell and an isolated process, and reportsprocess_exitedonly for a process that never connected.
BF-fa3da97-29
A tool that streams a growing partial result no longer sends the whole result to each subscribed extension on…
- Source: PiG's own defect
- Symptom: A tool that streams a growing partial result no longer sends the whole result to each subscribed extension on every update; updates carry only what changed, and handlers still receive the full
partialResult. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A tool that streams a growing partial result no longer sends the whole result to each subscribed extension on every update; updates carry only what changed, and handlers still receive the full
partialResult.
BF-fa3da97-3
done() of an overlay ui.custom component hides that component's own overlay instead of the topmost one
- Source: PiG's own defect
- Symptom:
done()of an overlayui.customcomponent hides that component's own overlay instead of the topmost one. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note:
done()of an overlayui.customcomponent hides that component's own overlay instead of the topmost one. With a shell overlay open under a dialog overlay from another extension, the shell's exit no longer removes the dialog or leaves the finished shell on screen (Pi #10999, #10648).
BF-fa3da97-30
A Durable core request costs the same however many requests are in flight: a notice is tested only against…
- Source: PiG's own defect
- Symptom: A Durable core request costs the same however many requests are in flight: a notice is tested only against the requests waiting for it.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A Durable core request costs the same however many requests are in flight: a notice is tested only against the requests waiting for it.
BF-fa3da97-31
Durable tool calls that the model issues in parallel or nested no longer slow down as the live state grows: a…
- Source: PiG's own defect
- Symptom: Durable tool calls that the model issues in parallel or nested no longer slow down as the live state grows: a commit no longer decodes the whole live document, and each tool reads only its own call.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Durable tool calls that the model issues in parallel or nested no longer slow down as the live state grows: a commit no longer decodes the whole live document, and each tool reads only its own call.
BF-fa3da97-32
With an extension turn_end handler, each tool result no longer copies the whole session history, and a…
- Source: PiG's own defect
- Symptom: With an extension
turn_endhandler, each tool result no longer copies the whole session history, and a boundary preview no longer re-encodes every entry: long sessions stay fast at turn ends. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: With an extension
turn_endhandler, each tool result no longer copies the whole session history, and a boundary preview no longer re-encodes every entry: long sessions stay fast at turn ends.
BF-fa3da97-33
A Durable reference harness turn no longer copies the whole conversation history to extend its context
- Source: PiG's own defect
- Symptom: A Durable reference harness turn no longer copies the whole conversation history to extend its context.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A Durable reference harness turn no longer copies the whole conversation history to extend its context.
BF-fa3da97-34
Durable: Submission.Wait, WaitForIdle and WaitForTask return only after every event-stream listener has…
- Source: PiG's own defect
- Symptom: Durable:
Submission.Wait,WaitForIdleandWaitForTaskreturn only after every event-stream listener has been given the batches committed before the wait, as in Pi. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Durable:
Submission.Wait,WaitForIdleandWaitForTaskreturn only after every event-stream listener has been given the batches committed before the wait, as in Pi. Stopping an event stream right after one of these waits no longer drops the end of a run.
BF-fa3da97-35
Selecting a go.work member module that is itself an exact factory package resolves that module's factory…
- Source: PiG's own defect
- Symptom: The selection scanned every member module and reported all of their factories as ambiguous, so a monorepo with one module per extension and a root
go.workcould not install any member. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Selecting a
go.workmember module that is itself an exact factory package resolves that module's factory and keeps the workspace as the compiler closure (#212). Before, the selection scanned every member module and reported all of their factories as ambiguous, so a monorepo with one module per extension and a rootgo.workcould not install any member. The workspace root itself stays ambiguous when several members hold factories. An exactly selected member that is apackage mainstandalone resolves to that member instead of "spans N modules".
BF-fa3da97-36
A --print or --mode json run that SIGTERM or SIGHUP reaches while its bash tool is starting the shell no…
- Source: PiG's own defect
- Symptom: A
--printor--mode jsonrun that SIGTERM or SIGHUP reaches while its bash tool is starting the shell no longer has a data race onos.Stdout. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A
--printor--mode jsonrun that SIGTERM or SIGHUP reaches while its bash tool is starting the shell no longer has a data race onos.Stdout. Starting a child process (a shell, an extension command, a Durable child) decided which of the child's files to leave open by reading theos.Stdoutvariable, which the exit path's restore of the output guard assigns at the same time. It now compares them with the filesos.Stdin,os.Stdoutandos.Stderrheld when pig started, which stay open as before. The output and exit status are unchanged.
BF-fa3da97-37
chord/host: the Node facet loader no longer writes its child entry under one fixed name in the shared…
- Source: PiG's own defect
- Symptom:
chord/host: the Node facet loader no longer writes its child entry under one fixed name in the shared temporary directory. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note:
chord/host: the Node facet loader no longer writes its child entry under one fixed name in the shared temporary directory. Another process that cleaned that file up, or two processes racing it, made a starting load fail withchild exited before ready; and any other user could rewrite the file Node ran. Each load now writes the entry into a private0700directory of its own and removes it once the child has started.
BF-fa3da97-38
A Durable core listing of all or terminal tasks (ScanTasks) reads one page from storage per call instead of…
- Source: PiG's own defect
- Symptom: A Durable core listing of all or terminal tasks (
ScanTasks) reads one page from storage per call instead of loading every task: a page costs the same at any task count. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A Durable core listing of all or terminal tasks (
ScanTasks) reads one page from storage per call instead of loading every task: a page costs the same at any task count. Listing a conversation owner's conversations reads only that owner's.
BF-fa3da97-39
Edit diffs and the edit tool with many replacements on a large file use far less memory and time: a…
- Source: PiG's own defect
- Symptom: Edit diffs and the
edittool with many replacements on a large file use far less memory and time: a 16,000-line diff takes a quarter of the time and 2 MB instead of 336 MB. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Edit diffs and the
edittool with many replacements on a large file use far less memory and time: a 16,000-line diff takes a quarter of the time and 2 MB instead of 336 MB.
BF-fa3da97-4
A call through a remote Chord reference with a ctx from another host, or one not made by Chord, fails with…
- Source: PiG's own defect
- Symptom: A call through a remote Chord reference with a
ctxfrom another host, or one not made by Chord, fails withinvalid/foreign_ctxbefore anything crosses the link, like a call through a local reference. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A call through a remote Chord reference with a
ctxfrom another host, or one not made by Chord, fails withinvalid/foreign_ctxbefore anything crosses the link, like a call through a local reference.
BF-fa3da97-40
An extension that set its header or footer, or registered a sprite or a login, from its own goroutine painted…
- Source: PiG's own defect
- Symptom: An extension that set its header or footer, or registered a sprite or a login, from its own goroutine painted the whole screen on that goroutine while the main loop was changing the transcript, so the two could read and write the same transcript state at once.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: An extension that set its header or footer, or registered a sprite or a login, from its own goroutine painted the whole screen on that goroutine while the main loop was changing the transcript, so the two could read and write the same transcript state at once. Such a change now asks the main loop for a frame, as Pi's
setExtensionHeaderandsetExtensionFooterend withui.requestRender(); a change made on the main loop still paints at once. The race detector reported it in about one run in ten of the Node scroll-view footer scenario, which sets its footer while a notification arrives.
BF-fa3da97-41
The terminal width and height the extension host tells extensions during /reload, and when one starts or…
- Source: PiG's own defect
- Symptom: The terminal width and height the extension host tells extensions during
/reload, and when one starts or restarts, were read while the main loop's frame updated them, and the handler that reports an extension's crash was replaced on each reload while the host's connection goroutines read it. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Crash, hang or runaway process
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: The terminal width and height the extension host tells extensions during
/reload, and when one starts or restarts, were read while the main loop's frame updated them, and the handler that reports an extension's crash was replaced on each reload while the host's connection goroutines read it. Both now read an atomically published value. The race detector reported the width and height read in about one/reloadrun in twenty, and the crash handler once, in a shutdown from asession_starthandler.
BF-fa3da97-42
Deleting members of a large Durable core JSON object no longer costs time proportional to its size for each…
- Source: PiG's own defect
- Symptom: Deleting members of a large Durable core JSON object no longer costs time proportional to its size for each delete.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Deleting members of a large Durable core JSON object no longer costs time proportional to its size for each delete.
BF-fa3da97-5
Turning thinking off on Claude Haiku 5.5 and Opus 5 sends plain thinking: {type: "disabled"} without effort…
- Source: PiG's own defect
- Symptom: Turning thinking off on Claude Haiku 5.5 and Opus 5 sends plain
thinking: {type: "disabled"}without effort markers, which Anthropic rejects while thinking is disabled. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Turning thinking off on Claude Haiku 5.5 and Opus 5 sends plain
thinking: {type: "disabled"}without effort markers, which Anthropic rejects while thinking is disabled.
BF-fa3da97-6
The retry classifier treats "The service is temporarily unavailable" and Bedrock's "Service unavailable: ..."…
- Source: PiG's own defect
- Symptom: The retry classifier treats "The service is temporarily unavailable" and Bedrock's "Service unavailable: ..." as retryable.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: The retry classifier treats "The service is temporarily unavailable" and Bedrock's "Service unavailable: ..." as retryable.
BF-fa3da97-7
A Progress pause longer than Node's timer maximum, or not a number, runs after 1 ms as a Node timer does
- Source: PiG's own defect
- Symptom: A
Progresspause longer than Node's timer maximum, or not a number, runs after 1 ms as a Node timer does. - Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A
Progresspause longer than Node's timer maximum, or not a number, runs after 1 ms as a Node timer does.
BF-fa3da97-8
Anthropic and Claude on Bedrock no longer lose the images of an error tool result: Pi sends them after the…
- Source: PiG's own defect
- Symptom: Anthropic and Claude on Bedrock no longer lose the images of an error tool result: Pi sends them after the tool results of the turn, under a caption naming the tool call, instead of replacing them with a note (#10794).
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: Anthropic and Claude on Bedrock no longer lose the images of an error tool result: Pi sends them after the tool results of the turn, under a caption naming the tool call, instead of replacing them with a note (#10794). Other Bedrock models keep the images in the result.
BF-fa3da97-9
A Durable tool call aborted while the Harness was still resolving the tool to run could stay running for…
- Source: PiG's own defect
- Symptom: A Durable tool call aborted while the Harness was still resolving the tool to run could stay running for good, with the generation that called it waiting on it.
- Root cause: changelog.d held 62 fragments in legacy.txt, which exempts them from the marker rule.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.2
- Fix commits:
fa3da978e2(2026-10-22) - Tests: None recorded.
- Release note: A Durable tool call aborted while the Harness was still resolving the tool to run could stay running for good, with the generation that called it waiting on it. The abort cancelled the call before its tool started, and the native core driver ended the call without telling the core, which settles a cancelled call only on that report. The call now reports the cancelled lookup as a thrown result, as it reports a tool that observes the cancel. It stalled once in about 1,000 CI runs of the abort-with-generation test under the race detector.
BF-fac4703
The extension editor dialog (ctx.ui.editor(), /tree summary prompt) shows the hint keys the user bound…
- Source: PiG's own defect
- Symptom: The extension editor dialog (
ctx.ui.editor(),/treesummary prompt) shows the hint keys the user bound for submit, newline and cancel instead of the default key names (internal/codingagent/extension_editor.go, extension-editor.ts keyHint; extendedTestExtensionEditorHandleInputMatchesPirenders the hint row under five rebindings, red before). - Root cause: Pi builds the hint with keyHint(action), which resolves tui.select.confirm, tui.input.newLine and tui.select.cancel against the current bindings; Pig printed the default key names. Reverting the hint line fails them.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),211af490e0(2026-10-11),4ae159f3cd(2026-10-11),a2755058da(2026-10-11),fac470319e(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,coding/discover_extensions_oracle_test.go::TestDiscoverExtensionPathsMatchPi,coding/extension/host/inproc/shortcuts_oracle_test.go::TestRunnerShortcutsMatchPi,internal/codingagent/armin_test.go::TestArminFramesMatchPinnedPi,internal/codingagent/bug_report_files_oracle_test.go::TestBugReportFilesMatchPiand 14 more in docs/bug-ledger.json - Release note: The extension editor dialog (
ctx.ui.editor(),/treesummary prompt) shows the hint keys the user bound for submit, newline and cancel instead of the default key names (internal/codingagent/extension_editor.go, extension-editor.ts keyHint; extendedTestExtensionEditorHandleInputMatchesPirenders the hint row under five rebindings, red before).
BF-fac4703-2
TestSettingsSelectorMatchesPi walks every row under rebound, multi-key and unbound app.message.followUp /…
- Source: PiG's own defect
- Symptom:
TestSettingsSelectorMatchesPiwalks every row under rebound, multi-key and unboundapp.message.followUp/app.thinking.cycleand rebound select keys, so the row descriptions that name those keys are compared; no difference; fixed-key mutations on both descriptions are red. - Root cause: Pi builds the hint with keyHint(action), which resolves tui.select.confirm, tui.input.newLine and tui.select.cancel against the current bindings; Pig printed the default key names. Reverting the hint line fails them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
6a6fffa638(2026-10-10),33fe10d58e(2026-10-11),fac470319e(2026-10-11) - Tests:
agent/message_json_number_spelling_test.go::TestAgentMessageDecodeKeepsANonNumberWhereAnIntegerIsExpected,agent/message_json_test.go::TestAgentMessageJSONRejectsInvalidUnion,agent/message_type_mismatch_test.go::TestDecodedMessageKeepsMembersOfAnyType,agent/provider_content_block_decode_test.go::TestUnknownContentBlockCostsOnlyThatBlock,ai/models_error_oracle_test.go::TestModelsErrorMessageMatchesPi,coding/mcpext/manager_view_oracle_test.go::TestMcpManagerViewMatchesPi,internal/codingagent/bash_execution_oracle_test.go::TestBashExecutionComponentMatchesPi,internal/codingagent/extension_editor_test.go::TestExtensionEditorHandleInputMatchesPiand 10 more in docs/bug-ledger.json - Release note:
TestSettingsSelectorMatchesPiwalks every row under rebound, multi-key and unboundapp.message.followUp/app.thinking.cycleand rebound select keys, so the row descriptions that name those keys are compared; no difference; fixed-key mutations on both descriptions are red.
BF-fac4703-3
TestSelectListInputMatchesPi also runs under the Kitty keyboard protocol (CSI-u enter, escape, ctrl+j…
- Source: PiG's own defect
- Symptom:
TestSelectListInputMatchesPialso runs under the Kitty keyboard protocol (CSI-u enter, escape, ctrl+j, ctrl+c, shift+enter, modified arrows, rebound select keys); no difference; a literal-LF confirm mutation is red only in Kitty mode. - Root cause: Pi builds the hint with keyHint(action), which resolves tui.select.confirm, tui.input.newLine and tui.select.cancel against the current bindings; Pig printed the default key names. Reverting the hint line fails them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
fac470319e(2026-10-11) - Tests:
coding/mcpext/manager_view_oracle_test.go::TestMcpManagerViewMatchesPi,internal/codingagent/extension_editor_test.go::TestExtensionEditorHandleInputMatchesPi,internal/codingagent/llama/ui_oracle_test.go::TestLlamaViewMatchesPi,internal/codingagent/mcp_cards_oracle_test.go::TestMcpToolCardsMatchPi,tui/extension_input_oracle_test.go::TestExtensionInputMatchesPi,tui/extension_selector_oracle_test.go::TestExtensionSelectorMatchesPi,tui/select_list_oracle_test.go::TestSelectListInputMatchesPi - Release note:
TestSelectListInputMatchesPialso runs under the Kitty keyboard protocol (CSI-u enter, escape, ctrl+j, ctrl+c, shift+enter, modified arrows, rebound select keys); no difference; a literal-LF confirm mutation is red only in Kitty mode.
BF-fba9b47
fix(extension): hold the theme colors map the wire palette memo is keyed on
- Source: PiG's own defect
- Symptom: fix(extension): hold the theme colors map the wire palette memo is keyed on
- Root cause: ActiveExtensionThemeWire identified the terminal-resolved colors by the address of the map theme.Colors returned, stored as a uintptr. The theme drops that map when the terminal's reported colors change, so the collector could free it and give its address to a later colors map with other values; the memo would then return the old palette and the state push would skip the theme as unchanged. The memo now holds the map it is keyed on, so the address cannot be reused while it identifies the palette.
- User impact: Wrong display
- Platform: All platforms
- Fixed in: Unreleased
- Fix commits:
fba9b473e9(2026-10-11) - Tests:
internal/codingagent/ext_ui_theme_palette_test.go::TestActiveExtensionThemeWireHoldsTheColorsMapItIsKeyedOn
BF-fbd3640
The legacy credential migration writes auth.json and the rewritten settings.json with the key order Pi…
- Source: PiG's own defect
- Symptom: The files used to be written with every key sorted.
- Root cause: migrations.ts:60,69 write JSON.stringify(settings|migrated, null, 2): keys keep Object.entries order and <, > and & stay unescaped. The Go port marshalled Go maps, which sorted every key.
- User impact: Credential or permission exposure
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
8d23cfcee3(2026-10-06),fbd3640da9(2026-10-06) - Tests:
internal/codingagent/migrations_auth_order_test.go::TestMigrateAuthToAuthJSONWritesFilesInPiKeyOrder,internal/codingagent/settings_key_order_test.go::TestSaveSettingsPatchKeepsTheFileKeyOrder,internal/codingagent/settings_key_order_test.go::TestSaveSettingsPatchReplacesInPlace - Release note: The legacy credential migration writes
auth.jsonand the rewrittensettings.jsonwith the key order Pi writes (array-index names first, then the order in the old files) and without escaping<,>and&. The files used to be written with every key sorted.
BF-fc5baaf
pi.exec's timeout starts once the command has been spawned, as in Pi, so time spent finding and starting…
- Source: PiG's own defect
- Symptom:
pi.exec'stimeoutstarts once the command has been spawned, as in Pi, so time spent finding and starting the program no longer counts against it. - Root cause: exec.ts:75 sets the timer after spawn has returned, so finding and starting the program does not count against options.timeout. ExecCommand started it before the PATH search and the fork, so a slow spawn (a long PATH, a loaded host) killed the child before it ran where Pi lets it finish.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
fc5baaf077(2026-10-13) - Tests:
coding/extension/exec_oracle_test.go::TestExecCommandMatchesPi,coding/extension/exec_timeout_unix_test.go::TestExecCommandStartsTheTimeoutAfterTheSpawnAsPiDoes - Release note:
pi.exec'stimeoutstarts once the command has been spawned, as in Pi, so time spent finding and starting the program no longer counts against it.
BF-fc749ad
A task list item whose first line looks like a heading, quote, rule, bullet, fence, table row or HTML tag…
- Source: PiG's own defect
- Symptom: A task list item whose first line looks like a heading, quote, rule, bullet, fence, table row or HTML tag renders that line as text, and a setext heading over it keeps the checkbox marker, as Pi does.
- Root cause: marked splits
[ ]into a checkbox token and lexes the rest of the first line as a text token, so- [ ] # h,- [ ] > q,- [ ] ---,- [ ] - a, a fence, a table row, a definition or an HTML tag renders as text, while the lines after it are lexed normally; a setext heading over the first line (- [ ] t/===) takes the marker into its text and the item has no checkbox. Pig stripped the marker and rendered the block. renderList now keeps the marker when setextHeadingEnd matches the original text, and otherwise lexes the first block with a leading control character that no block rule matches, removed from the rendered rows. - User impact: Wrong display
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
fc749adcb9(2026-10-11) - Tests:
tui/markdown_task_item_oracle_test.go::TestMarkdownTaskItemFirstLineIsTextMatchesPi - Release note: A task list item whose first line looks like a heading, quote, rule, bullet, fence, table row or HTML tag renders that line as text, and a setext heading over it keeps the checkbox marker, as Pi does.
BF-fc886e3
Google and Vertex request bodies list their members in the order the @google/genai SDK writes them, also…
- Source: PiG's own defect
- Symptom: Google and Vertex request bodies list their members in the order the
@google/genaiSDK writes them, also when an extension handlesbefore_provider_request: body members (contents,systemInstruction,tools,toolConfig, thengenerationConfig),generationConfigmembers,partsbeforerole, and the members of each part and function call. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),2309604d80(2026-10-10),b4e83dbe17(2026-10-10),c10cde302d(2026-10-10),eeac307095(2026-10-10),f29e906cb3(2026-10-10),fc886e3bd2(2026-10-10),fe16108cc7(2026-10-10) - Tests:
agent/assistant_member_order_oracle_test.go::TestAssistantLateMembersFollowPiCreationOrder,agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowClosesand 18 more in docs/bug-ledger.json - Release note: Google and Vertex request bodies list their members in the order the
@google/genaiSDK writes them, also when an extension handlesbefore_provider_request: body members (contents,systemInstruction,tools,toolConfig, thengenerationConfig),generationConfigmembers,partsbeforerole, and the members of each part and function call. A function response listspartsbeforeid, as Pi does.
BF-fce99d3
The "Your first ten minutes" and Quickstart pages now describe the first-time setup screen (theme, sprite and…
- Source: PiG's own defect
- Symptom: They said PiG had no setup screen.
- Root cause: Not recorded.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
fce99d3151(2026-10-11) - Tests: None recorded.
- Release note: The "Your first ten minutes" and Quickstart pages now describe the first-time setup screen (theme, sprite and usage data) that PiG shows on a fresh install (D88). Before, they said PiG had no setup screen. The first-run page also explains that
Enterin the model list picks a model for one conversation andCtrl+Smakes it the default, what the footer fields mean, and what a401error after/loginmeans.
BF-fce99d4
An npm or git -e source that fails to install stops startup and /reload before any extension factory…
- Source: PiG's own defect
- Symptom: An npm or git
-esource that fails to install stops startup and/reloadbefore any extension factory runs, and later-esources are not installed. - Root cause: One pattern's result varies between Node runs, because the length limit counts a random marker. 24 of 26 compiled mutants fail a test. The other two move the length limit's boundary inside a combine before the last, or drop it from a sequence; a later combine meets the same limit first, so the results cannot differ.
- User impact: An operation failed or was rejected
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
311609427b(2026-10-07),a79de03e7f(2026-10-07),d0ad06fc18(2026-10-07),1cbb6b6fb2(2026-10-09),4401fa4630(2026-10-09),6584435bb6(2026-10-09),6e6b9132c2(2026-10-09),d586d71f85(2026-10-09),ef007a4e5e(2026-10-09),6410ef49f6(2026-10-10),6f8d83861a(2026-10-10),fce99d43d7(2026-10-10) - Tests:
coding/cli/cli_package_glob_test.go::TestCLIGitSourceRefreshesOncePerLoad,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceGlobThrowFirstSourceWins,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceGlobThrowFollowsPi,coding/cli/cli_package_glob_test.go::TestCLIPackageSourceInstallFailureStopsStartup,coding/cli/cli_package_glob_test.go::TestReloadSnapshotReportsCLIPackageGlobThrow,coding/cli/cli_package_glob_test.go::TestStartupStopsAtACLIPackageGlobThrow,coding/cli/package_update_cases_upstream_test.go::TestPackageUpdateLookupPrefixesOriginal,coding/cli/settings_file_url_upstream_test.go::TestNpmPackageManifestGlobThrowRejectsResolutionand 55 more in docs/bug-ledger.json - Release note: An npm or git
-esource that fails to install stops startup and/reloadbefore any extension factory runs, and later-esources are not installed. Each-esource resolves once per load.
BF-fd4be86
OpenAI-compatible Chat Completions keeps the "text":"" member of a user text part whose text is only a lone…
- Source: PiG's own defect
- Symptom: OpenAI-compatible Chat Completions keeps the
"text":""member of a user text part whose text is only a lone UTF-16 surrogate, as Pi writes it after sanitizing (a part that is empty to begin with is still dropped). - Root cause: Not recorded.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
fd4be8683c(2026-10-11) - Tests:
ai/openai_completions_empty_sanitized_text_test.go::TestCompletionsUserTextThatSanitizesToNothingKeepsItsEmptyTextMember - Release note: OpenAI-compatible Chat Completions keeps the
"text":""member of a user text part whose text is only a lone UTF-16 surrogate, as Pi writes it after sanitizing (a part that is empty to begin with is still dropped). Found by widening the payload oracles' random transcripts with lone surrogates and decoding and encoding them with the SDK-compatible JSON package.
BF-fd7f660
A resumed session keeps the tools that were active when it was saved (an optional or deferred tool a command…
- Source: PiG's own defect
- Symptom: A resumed session keeps the tools that were active when it was saved (an optional or deferred tool a command or extension had enabled no longer drops out and gets recorded as removed on the next turn); an explicit
--toolslist,--no-toolsor--no-builtin-toolsstill wins (pi-mono #10785). - Root cause: Pi #10887 makes the AgentSession constructor restore the transcript's loadout whenever the session uses the default tools, applying the --tools +name/-name entries to it. NewSession now passes DefaultToolModifiers to restoreToolsFromTranscript instead of skipping the restore when modifiers are given. Divergence D166 is retired.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Commit that wrote the release note (the fix commit is not identified):
fd7f660ae3(2026-10-13) - Tests:
coding/cli/rpc_resume_tools_test.go::TestRPCResumeKeepsDynamicallyActivatedTools,coding/session_resume_default_tools_port_test.go::TestResumeRestoresTheRecordedLoadoutPort - Release note: A resumed session keeps the tools that were active when it was saved (an optional or deferred tool a command or extension had enabled no longer drops out and gets recorded as removed on the next turn); an explicit
--toolslist,--no-toolsor--no-builtin-toolsstill wins (pi-mono #10785).
BF-fdfb748
A surrogate pair split across two concatenated string halves is written as its character in session files…
- Source: PiG's own defect
- Symptom: A surrogate pair split across two concatenated string halves is written as its character in session files, extension frames and Durable records, as Pi's
JSON.stringifywrites it, instead of as two\uescapes. - Root cause: Sibling of (durable/core/jv). The shared host/Go-SDK codec writes WTF-8 surrogate units as \u escapes; a high unit directly followed by a low unit (two WTF-8 halves concatenated in Go) is one code point in a JavaScript string, and JSON.stringify writes the character (node 24: JSON.stringify("\ud83d" + "\ude00") === '"😀"'). appendString now joins such a pair into its four-byte UTF-8; unpaired units stay escapes. This codec writes session entries (jsstring.MarshalJSON), extension frames and Durable storage.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
89338b7eac(2026-10-10),fdfb74849c(2026-10-10),fe16108cc7(2026-10-10) - Tests:
ai/openai_request_key_order_oracle_test.go::TestOpenAICompletionsRequestKeysFollowPiAssignmentOrder,ai/openai_responses_request_key_order_oracle_test.go::TestOpenAIResponsesRequestKeysFollowPiAssignmentOrder,ai/perf_gate_norace_test.go::TestProviderRequestHistoryAllocationGate,ai/request_body_escaping_test.go::TestProviderRequestBodiesWriteCharactersAsJSONStringifyDoes,durable/core/jv/split_pair_test.go::TestAppendStringJoinsASplitSurrogatePair,extensions/sdk/json/surrogate_test.go::TestSplitSurrogatePairEncodesAsItsCharacter,internal/jsstring/unescape_stringify_test.go::TestUnescapeForStringifyRewritesOnlyRealEscapes - Release note: A surrogate pair split across two concatenated string halves is written as its character in session files, extension frames and Durable records, as Pi's
JSON.stringifywrites it, instead of as two\uescapes.
BF-fe16108
Provider request bodies (OpenAI Completions, OpenAI Responses, Codex, Anthropic, Google and Mistral) write…
- Source: PiG's own defect
- Symptom: Provider request bodies (OpenAI Completions, OpenAI Responses, Codex, Anthropic, Google and Mistral) write
<,>,&, U+2028 and U+2029 as they are, as Pi'sJSON.stringifydoes, instead of as\uescapes. - Root cause: Pi's provider SDKs send JSON.stringify(body) (openai 7.19.0 internal/request-options.js:29; the Anthropic, Google GenAI and Mistral SDKs stringify the same way), which writes these five characters as they are. PiG wrote every body with encoding/json.Marshal, which escapes them, so a user message "a <b> & c" went out as "a \u003cb\u003e \u0026 c".
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
f29e906cb3(2026-10-10),fe16108cc7(2026-10-10) - Tests:
ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPi,ai/openai_request_key_order_oracle_test.go::TestOpenAICompletionsRequestKeysFollowPiAssignmentOrder,ai/openai_responses_request_key_order_oracle_test.go::TestOpenAIResponsesRequestKeysFollowPiAssignmentOrder,ai/perf_gate_norace_test.go::TestProviderRequestHistoryAllocationGate,ai/request_body_escaping_test.go::TestProviderRequestBodiesWriteCharactersAsJSONStringifyDoes,internal/jsstring/unescape_stringify_test.go::TestUnescapeForStringifyRewritesOnlyRealEscapes - Release note: Provider request bodies (OpenAI Completions, OpenAI Responses, Codex, Anthropic, Google and Mistral) write
<,>,&, U+2028 and U+2029 as they are, as Pi'sJSON.stringifydoes, instead of as\uescapes.
BF-fe625bb
Mistral request bodies list their members in Pi's order (model, stream, messages, ..., then the renamed…
- Source: PiG's own defect
- Symptom: Mistral request bodies list their members in Pi's order (
model,stream,messages, ..., then the renamedmax_tokens,tool_choice,reasoning_effort,prompt_mode,prompt_cache_key; a tool result'stool_call_idlast; thinking parts astype,text) instead of sorted, and the body is built about 3 times faster. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
433d232815(2026-10-09),afa1dd5185(2026-10-10),d8c227b7f5(2026-10-10),f02269c6e1(2026-10-10),fe625bb5dd(2026-10-10) - Tests:
agent/assistant_member_order_oracle_test.go::TestAssistantLateMembersFollowPiCreationOrder,agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowClosesand 17 more in docs/bug-ledger.json - Release note: Mistral request bodies list their members in Pi's order (
model,stream,messages, ..., then the renamedmax_tokens,tool_choice,reasoning_effort,prompt_mode,prompt_cache_key; a tool result'stool_call_idlast; thinking parts astype,text) instead of sorted, and the body is built about 3 times faster.
BF-fe625bb-2
When an extension's before_provider_request handler returns a replacement body for OpenAI Completions…
- Source: PiG's own defect
- Symptom: When an extension's
before_provider_requesthandler returns a replacement body for OpenAI Completions, OpenAI Responses, Codex, Pi Messages, Mistral or Google, PiG sends it as Pi'sJSON.stringifywrites whatJSON.parseread:\u00e9becomesé,1.50becomes1.5, a repeated key keeps its first place with its last value, and array-index keys come first. - Root cause: After /tree navigation between parallel tool calls and their results, a branch summary followed by a new prompt sent a synthetic tool_result behind the summary, and Anthropic rejected it with an unexpected tool_use_id. Upstream runs transformMessages after convertToLlm, where a branch summary, compaction summary, bash execution or custom message is already a user message and closes the pending tool calls. NormalizeMessages ran first and did not see them.
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
e8cc072521(2026-10-08),433d232815(2026-10-09),afa1dd5185(2026-10-10),f02269c6e1(2026-10-10),fe625bb5dd(2026-10-10) - Tests:
agent/convert_custom_upstream_test.go::TestConvertToLLMCustomMessagesMatchUpstreamConvertToLlm,agent/convert_custom_upstream_test.go::TestConvertToLLMToolFlowMatchesUpstreamTransformOrder,agent/convert_custom_upstream_test.go::TestCustomMessagesReachProvidersAsUpstreamContentBlocks,agent/transform_custom_user_boundary_test.go::TestAnthropicRequestAfterBranchSummaryHasNoOrphanToolResult,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomMessageWithoutWireOutputKeepsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMCustomUserMessageInterruptsToolFlow,agent/transform_custom_user_boundary_test.go::TestConvertToLLMHoldsSystemMessageUntilToolFlowCloses,ai/anthropic_messages_payload_oracle_test.go::TestAnthropicMessagesPayloadMatchesPiand 19 more in docs/bug-ledger.json - Release note: When an extension's
before_provider_requesthandler returns a replacement body for OpenAI Completions, OpenAI Responses, Codex, Pi Messages, Mistral or Google, PiG sends it as Pi'sJSON.stringifywrites whatJSON.parseread:\u00e9becomesé,1.50becomes1.5, a repeated key keeps its first place with its last value, and array-index keys come first. PiG sent the handler's text unchanged.
BF-ffcd800
Text after an empty heading follows the same space-token rule as after any other block: blank lines or spaces…
- Source: PiG's own defect
- Symptom: Text after an empty heading follows the same space-token rule as after any other block: blank lines or spaces after a final empty heading add the empty row Pi shows.
- Root cause: marked trims a heading's raw of its newlines, so the text after it is a space token when longer than one character, as after any block ('# \n\n', '#\n ', 'a\n#\n ' render two or more rows in Pi). The earlier guard that kept the heading's own row from a trailing token is replaced by one for the row a list already added. 9 new rows (6 red before).
- User impact: Behavior differed from Pi
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
2a919a4c3c(2026-10-10),4e5b684ecd(2026-10-10),ac0dc3bee4(2026-10-10),cdf0be76e4(2026-10-10),ffcd800f73(2026-10-10) - Tests:
tui/markdown_render_oracle_test.go::TestMarkdownRenderMatchesPi - Release note: Text after an empty heading follows the same space-token rule as after any other block: blank lines or spaces after a final empty heading add the empty row Pi shows.
BF-ffeb469
A long session keeps less memory for each turn it has compacted away
- Source: PiG's own defect
- Symptom: A long session keeps less memory for each turn it has compacted away.
- Root cause: A 21,000-turn RPC session with automatic compaction grew its live heap by 1.75 KiB per turn. Three parts of that were not entries Pi also keeps: - the /tree search text of an entry compaction moved out of the context was built at eviction (about 340 B per turn); it is now read from the entry's record on the first search, as after an open - the ai tool-call argument encoding cache kept the encodings of dropped calls until its 16 MiB limit (about 480 B per turn); an encoding is now released when its arguments are unreachable - agent.Recorder appended a duration per turn; it keeps the count and the last duration
- User impact: Wrong output or ordering
- Platform: All platforms
- Fixed in: 0.5.0
- Fix commits:
ffeb469492(2026-10-14) - Tests:
agent/timings_test.go::TestRecorderRetainsNothingPerTurn,agent/timings_test.go::TestRecorderTurnRoundTrip,ai/tool_call_arguments_lifetime_test.go::TestArgumentEncodingsAreReleasedWithTheirArguments,ai/tool_call_arguments_lifetime_test.go::TestToolCallArgumentsJSONKeepsNothingForADroppedCall,coding/extension/host/subprocess/runtime_node_sdk_bundle_test.go::TestNodeLibraryBundlesRegenerateExactly,coding/extension/host/subprocess/runtime_node_sdk_bundle_test.go::TestNodeSDKBundleRegeneratesExactly,internal/codingagent/session_retained_growth_norace_test.go::TestSessionKeepsNoSearchTextForEntriesNobodySearched,internal/codingagent/session_retained_growth_norace_test.go::TestSessionLiveHeapPerExchange - Release note: A long session keeps less memory for each turn it has compacted away. The
/treesearch text of an entry that compaction moves out of the context is no longer built and kept when the entry leaves memory: the first search reads it from the entry's record, as it already did after opening a session, and a session nobody searches keeps none. The cache of encoded tool-call arguments releases an encoding when the arguments it encodes are no longer reachable, where it kept the encodings of dropped calls until its 16 MiB limit. The agent's turn timer keeps the number of completed turns and the last duration instead of one duration per turn. In a 21,000-turn RPC session with automatic compaction on a 32k window, live heap growth falls from about 1.7 KiB per turn to the index of the session's entries (slots, compressed records, compaction entries), which Pi's in-memory session also keeps, as whole entries.
Divergences from Pi added in this release
Each divergence record is accounted for here. A divergence that fixes a Pi defect links to its ledger entry; the others are deliberate differences and no bug fix.
Divergence | Title | Ledger entry |
| D96 | The remote execution daemon is a Go program, not Pi's Rust binary | deliberate difference |
| D98 | The eval harness hands the agent process an OAuth credential file | deliberate difference |
| D99 | The Windows npm self-update quarantine waits out a held image | BF-f288d69-2 |
| D103 | JS Error.stack is not modelled | deliberate difference |
| D105 | The interactive driver owns terminal input; extension input listeners run asynchronously | deliberate difference |
| D110 | Git sources: #subdirectory= selects a subdirectory; git:// and upper-case schemes yield usable… | BF-D110 |
| D111 | Abort abandons a non-cooperative extension tool after a grace period | BF-9be5765 |
| D113 | A Session has no extensionRunnerRef cell | deliberate difference |
| D114 | StdinBuffer has no getMaxListeners, setMaxListeners or MaxListenersExceededWarning | deliberate difference |
| D115 | Gemini 2.x tool-result images follow the whole run of function responses | BF-80bb522-2 |
| D116 | Bedrock Converse messages of one role are merged | BF-80bb522-3 |
| D117 | DiscoverAndLoadExtensions takes no event bus | deliberate difference |
| D118 | A durable commit with an unknown task or submission status fails with "Unknown task status" | deliberate difference |
| D120 | A streamed tool call without an id or name no longer wedges the session (pi-mono #10299) | BF-a3859aa |
| D121 | The Anthropic SSE reader holds back a carriage return that ends a chunk (pi-mono #10390) | BF-1fbdabb |
| D122 | OpenAI Completions keeps a signature-only reasoning.text delta (pi-mono #9534) | BF-cb00273 |
| D128 | OpenRouter image models that need the Images API are retried through /images (pi-mono #10652) | BF-b730205 |
| D129 | A Responses function call streamed with an empty call_id no longer wedges the session (pi-mono #10299) | BF-0dff068 |
| D130 | A managed-effort Anthropic request sends each managed field only with its beta (Pi #10759) | BF-5e3e346 |
| D131 | A stalled SSE stream that still sends keepalives ends after the HTTP idle timeout (pi-mono #10761, #10762) | BF-3651a90 |
| D132 | Session directories are created with mode 0700 (pi-mono #10770) | BF-25c854d |
| D134 | One SSE line is bounded at Node's maximum string length | deliberate difference |
| D135 | clearQueue keeps queued extension custom messages (Pi #9886) | BF-f98d129 |
| D136 | A context edit after the usage anchor keeps the anchor (Pi #10287) | BF-f98d129-2 |
| D137 | A request carries at most the model's maxPerRequest images (Pi #10162) | BF-f98d129-3 |
| D138 | session_compact custom messages reach the overflow retry (Pi #9051) | BF-f19efb1 |
| D142 | Settlement keeps idle waiters pending and abort discards deferred prompts (Pi #10704, #10705) | BF-31740ec |
| D143 | A device-attributes reply split across slow reads is not forwarded as input (Pi #10657) | BF-0225a43 |
| D144 | A dropped response stream keeps its cause in the error message (Pi #10697) | BF-f07d033 |
| D145 | Nested Errors keep their name and message in codemode output (Pi #10650) | BF-35e46b1 |
| D146 | RPC stdin end answers a Session-replacing command that Pi leaves unanswered | deliberate difference |
| D147 | Compactions requested in one input batch run one after another | deliberate difference |
| D148 | An aborted compaction answers one error; two Pi defects of overlapping compacts are not reproduced | deliberate difference |
| D150 | --no-stdin skips the startup read of piped stdin (Pi #10415) | BF-D150 |
| D151 | grep with context reads a window of each matched file (Pi #9276) | BF-D151 |
| D153 | The bash truncation notice names the size of the last line, not the bytes after the final newline (Pi #10015) | BF-D153 |
| D155 | Selection lists page with PageUp and PageDown (Pi #7629) | BF-D155 |
| D156 | MCP server url expands environment references (Pi #10654) | BF-D156 |
| D157 | PiG's startup additions stay quiet when the config root cannot be written | deliberate difference |
| D158 | A setting of the wrong JSON type is ignored (Pi 1.4.0 throws) | deliberate difference |
| D159 | A single grapheme wider than the editor's wrap width stays one chunk instead of exhausting the stack | deliberate difference |
| D165 | The durable image processor is native Go, not Photon's WebAssembly | deliberate difference |
| D167 | A history longer than the model's context window is summarized in chunks | BF-85908ae |
| D173 | Tool search matches Chinese, Japanese and Korean queries (Pi #10816) | BF-b99457f |
| D177 | The calls of a parallel durable tool round start in unspecified order | deliberate difference |
| D178 | Facet modules load in an isolated Node child, not in the host process | deliberate difference |
| D180 | A provider transport failure records its Go cause as a provider_transport_failure diagnostic | deliberate difference |
| D181 | The commits of an unawaited durable job are ordered by goroutine scheduling, not by Pi's microtask hops | deliberate difference |
| D182 | A proxy start event with an unusable content index ends the stream with an error | deliberate difference |
| D183 | A message delivered at the terminal proxy event is not changed by later events | deliberate difference |
| D184 | A script's unawaited tool call can be aborted before its tool starts, where Pi always starts it first | deliberate difference |
| D185 | A Kitty CSI-u Shift+letter with no shifted alternate decodes as the uppercase letter, where Pi keeps the base… | deliberate difference |
| D186 | A hung-up terminal under a session whose event loop holds nothing: Pi exits 0 or 129, PiG exits 129 | deliberate difference |
| D187 | A native SDK holds a batch's first host calls and responses in call and answer order, where Pi's single… | deliberate difference |
| D188 | Opening a store at an older Durable schema version keeps a copy of it first, where Pi upgrades it in place | deliberate difference |
| D190 | The built-in file mutation queue also orders mutations of one existing file reached through a hard link or a… | BF-648ff3e-13 |
| D195 | An empty AWS_BEARER_TOKEN_BEDROCK is no token, where Pi's AWS SDK treats the variable as set and the request… | BF-D195 |
| D196 | Under skip-auth a bearer token from the process environment is not sent over an http:// endpoint, where Pi's… | BF-D196 |
| D198 | The Go, Rust and Python Type has no Codec family (Codec, Decode, Encode, DecodeBuilder… | BF-D198 |
| D199 | PiG lists OpenRouter's :nitro and :floor routing variants beside every OpenRouter model, where Pi lists only… | deliberate difference |
| D200 | With no home directory PiG still runs the commands that load no resources, where Pi crashes at import, and… | BF-D200 |
| D203 | registerProvider with an empty apiKey leaves the provider unconfigured where Pi treats the empty string as… | BF-D203 |
Fix-titled commits that are not bugs in a release
- Failure found by the integration build of unreleased work: 2
- Fix reverted before release: 1
- Repair of a merge between development branches: 8
- Review follow-up to unreleased work: 3
- test-only: 1