🏑


  1. July 07, 2026
    1. πŸ”— cloudflare/capnweb capnweb@0.10.0 release

      Minor Changes

      • #185 0b20ec6 Thanks @ndisidore! - Add configurable receiver-side resource limits (RpcSessionOptions.limits) that cap bigint length, message nesting depth, and incoming message size to guard against untrusted-peer resource exhaustion (#184).

      Patch Changes

      • #190 6e5c562 Thanks @taylorodell! - Several correctness and robustness fixes:

        • Error deserialization no longer resolves an attacker-supplied error type name to an inherited Object.prototype member. ERROR_TYPES now has a null prototype, so a wire value such as ["error","constructor",...] no longer resolves to Object (which produced a String wrapper instead of an Error, bypassing instanceof Error checks), and a name like "toString" no longer resolves to a non-constructor and throws. Unknown names correctly fall back to Error.
        • Error deserialization now filters inherited Object.prototype keys (and toJSON) out of an error's own-property bag, matching the behavior already applied when deserializing plain objects. Keys such as __proto__, toString, and valueOf are no longer copied onto deserialized errors.
        • Resolving an import that has already been resolved now disposes the redundant resolution instead of overwriting (and leaking) the previous one.
        • The abort message handler now hands error handlers the unwrapped abort reason rather than the internal payload wrapper, matching the reject handler.
        • WebSocket close reasons longer than the 123-byte limit are now truncated on a UTF-8 character boundary, so aborting a session with a long reason no longer throws from WebSocket.close().
    2. πŸ”— WerWolv/ImHex Nightly Builds release

      Nightly

      dd389db Changelog

      • fix: Invalid format string use for unknown hex editor cell value
  2. July 06, 2026
    1. πŸ”— anthropics/claude-code v2.1.202 release

      What's changed

      • Added a "Dynamic workflow size" setting in /config for controlling how large Claude generally makes dynamic workflows (small/medium/large agent counts) β€” an advisory guideline, not an enforced cap
      • Added workflow.run_id and workflow.name OpenTelemetry attributes to telemetry emitted by workflow-spawned agents, so a workflow run's activity can be reconstructed from OTel data
      • Fixed a crash in the inline Ctrl+R history search when accepting or cancelling while the search was still scanning the history file
      • Fixed /rename on background sessions being reverted when the job restarts, which broke addressing the session by its new name
      • Fixed transient mTLS handshake failures when settings were re-applied during an in-place client certificate rotation
      • Fixed commands sent from Remote Control (mobile/web) into an interactive session failing with "Unknown command"
      • Fixed images and files sent from the Remote Control mobile or web app without a caption being silently dropped
      • Fixed the sign-in URL printed by claude auth login and claude mcp login --no-browser not being reliably clickable when it wraps over SSH β€” it is now emitted as a single hyperlink
      • Fixed opening a chat from claude agents sometimes failing with "currently running as a background agent" followed by a worker crash/respawn loop
      • Fixed workflow scripts with unicode quote escapes in strings being corrupted before parsing; workflow parse errors now show the offending line instead of always blaming TypeScript
      • Fixed voice dictation retrying in an unbounded loop when the microphone or audio recorder fails β€” repeated capture failures now pause voice input
      • Fixed /remote-control sessions showing the wrong permission mode in the mobile and web apps
      • Fixed resuming a session by name, or opening the resume picker, taking minutes and using a large amount of memory in repositories with many git worktrees
      • Fixed installer and updater downloads failing immediately with "aborted" when a proxy or network drops the connection mid-download β€” transient connection drops now retry
      • Fixed re-invoking an already-loaded skill appending a duplicate copy of its instructions to context
      • Improved /workflows agent list layout: wider titles, a dedicated time column, shorter model names, and no per-row tool-call counts
      • Improved MCP error messages: clearer error when a server config has url but no type, suggesting "type": "http" instead of the misleading "command: expected string"
      • Changed /review <pr> back to a fast single-pass review; use /code-review <level> <pr#> for the multi-agent review at a chosen effort level
    2. πŸ”— HexRaysSA/plugin-repository commits sync repo: +1 plugin, +1 release rss
      sync repo: +1 plugin, +1 release
      
      ## New plugins
      - [llm-explainer](https://github.com/pgarba/ida-llm-explainer) (1.3.0)
      
    3. πŸ”— backnotprop/plannotator PR #957 automated browser-test recordings release

      Playwright recordings of the parity suite run against the compiled plannotator binary from PR #957 head 849fda4. Hosted here because PR comments cannot take file attachments via API. Safe to delete after #957 merges.

    4. πŸ”— MetaBrainz Current ListenBrainz server issues/high load, Part 2 rss

      This follow-up post covers how and why our servers are still being impacted, an explanation and examples of what we consider "botting" in ListenBrainz - and an apology, because our last post had some baaad comms!

      Firstly, we are still experiencing massive server load from an increase in listen submissions related to the BTS fan exodus from last.fm (not all bad! more on this below) - and we are now being hit by another influx of AI scrapers.

      Our apologies to our users, new and old, as we continue to work to mitigate the extra load. Remain assured that listens are not being lost - our processing queue is lagging behind, so listens may take (up to) hours to appear.

      LB radio and the popularity endpoints have been temporarily disabled, to reduce server load.

      ListenBrainz server status/load

      Our ingestion queue for the past 2 weeks.

      The above screenshot shows our ingestion queue (listens being submitted directly to ListenBrainz) for the past 2 weeks, which usually sits around 0 with peaks at 10K. The highest peak during the last two weeks - the big mountain on the graph - is 484K. That is to show both that the listens are not lost and why they were lagging behind. " Your listen is now number 484,001 in line…"

      Our import queue for the past 2 weeks.

      The above screenshot shows our import queues (listens being submitted via our import function), in the same 2-week window. The values are "number of listens added to the ingestion queue per minute ". The purple line is lastfm imports. We usually sit around 1-2K, with peaks around 20K sustained for ~3 hours at worst. The peak in this graph is 180K - that's 180K listens added to the queue that minute. These values have not been reducing at the time of writing, but fluctuating depending on time of day.

      You can see these graphs, and the current length of the 'queue', in real-time on this Grafana dashboard (free account required).

      Where this increased load is coming from

      Our server issues are currently three-fold. One is good, the other two not so much.

      • Last.fm imports : This is the good problem! When a user syncs their account from last.fm, they're sometimes importing 20 years worth of listens at a time. Every listen that is added to ListenBrainz passes through our listen queue, where our system checks the attached metadata, and tries to match it to the right artist, album and song. As you can imagine, if hundreds or thousands of users add their entire last.fm history at once, it's quite the processing queue. This load is a sign that we are growing, and we don't mind at all. Though we also don't enjoy having to wait for listens to appear - if anyone wants to gift us some extra servers….

      • " Botting"/listen submission spamming: This is not good. A very small minority of BTS fan accounts are doing things like submitting a listen every few seconds, and/or submitting listens across multiple accounts at once, which has resulted in hundreds of thousands of extra listens being added to the queue. This goes against our code of conduct. Please scroll down to the end of this blog post for clarification on what behaviour that we consider botting.

      • AI scrapers : There's not much to say here. The MetaBrainz foundation offers all of our data as freely downloadable datasets, but these AI companies don't care - they set up scrapers to blindly hoover up the internet without respecting limits or ethical considerations. They go to great lengths to hide behind thousands of residential IPs, making them extremely difficult to detect and block (as blocking these IPs also blocks legitimate users). You can read more in our previous blog on the subject: We can’t have nice things… because of AI scrapers

      Bad comms!

      We - the MetaBrainz team - are very sorry for how our last post was written, and how it negatively affected so many people.

      We are used to speaking to a small audience of our existing users, and we have a wonderful BTS/K-pop community in *Brainz already, which made us be too relaxed with our tone.

      It is true that a small minority of fans recently started doing things like submitting a listen every few seconds, across many accounts, which has impacted our service for all users. But we <3 our K-pop community! So we never imagined that it would be thought that we are blaming all the fans of a band or even a whole genre. But that is not obvious from outside our small community and usual readership, and that is our mistake and something we should have considered before posting.

      We are a small team, and most of us are very… technically minded. We do not have a comms team, and will probably never have one, so we ask for you to be patient with us.

      On the other side of the coin, we had to filter a lot of abusive comments from the last blog post (both from ARMY and directed towards ARMY) and that is not the sort of community we are fostering here. We also are, and will continue to be, direct and up-front with our messaging, in accordance with our open ethos. If a group of users - even if it's a non-representative subset of a larger group - are not respecting our rules and are causing issues for everyone, we will say so, and we will __ share how and why. We will endeavour to do this with tact and awareness (which we failed to do in our last post).

      If that works for you, and the respect is shared both ways - welcome, new users! And if you are a BTS fan, congratulations on the new album release, and we look forward to seeing it continue to explode the global charts.

      What we consider 'botting'/bad behaviour in ListenBrainz

      First, please note that when we say "botting", we don't mean that the listener is not a real human! It refers to using digital tools/code/devices to submit song listens that don't reflect real listening.

      We also want to acknowledge that this might be counter to how some people want to submit and store listens, and may not match everyone's definition of 'real listening'. We are sorry that our systems cannot cater to every approach. We think it's awesome that the BTS community has their own app, B-CD, that caters to users who want to listen-max their love for BTS! We love to see it!

      The following are (just some) examples of what we consider unacceptable in ListenBrainz.

      • Submitting listens you haven 't listened to: If you are submitting a 1 minute song as 'listened' every 5 seconds, or any other timeframe that is far shorter than the song itself, we are likely to disable your account. We have paused 100 accounts doing this, so far, with the 'busiest' submitting 6,000+ listens a day for a few days - a full song 'listened to' every ~15 seconds, day and night. This adds up.

      • Submitting the same listens across multiple accounts/devices: Our systems expect 1 song submitted listen to equal 1 song going into your earballs. Having multiple accounts/devices each submitting the same listen doesn't get around this! If you are doing this we are likely to disable your accounts.

      • Doing both of the above at a large scale: "Ahhhhhhhhhhhhhhhhhhhhhhhh" - our servers.

      tl;dr if your 1 listen is becoming 2 or more listen submissions in ListenBrainz, in any way, it's likely that your account will be disabled. If you email us we are always happy to look at the situation and to reinstate accounts for people who agree to follow the ListenBrainz rules. Our apologies that we cannot proactively email everyone who we disable at this time, we are dealing with too many accounts to do so.

      If you read all the way down here, thank you and have a lovely week - your MetaBrainz and ListenBrainz team.

    5. πŸ”— openonion/connectonion Release v1.1.0 release

      Release v1.1.0

      Scheduled email you can actually change β€” list, cancel, and schedule replies. All verified live.

      What's New

      co outlook scheduled

      See a numbered queue of your pending deferred sends β€” what's queued and when it goes out.

      co outlook cancel <#>

      Stop a scheduled send before it goes out by deleting the pending draft. Works on outlook.com; some Exchange tenants return 403 β€” use Outlook's own Cancel Send there.

      co outlook reply --at

      Schedule threaded replies, e.g. co outlook reply --at +2h, and they stay in the original conversation thread.

      Better reply formatting

      Reply bodies are converted to proper HTML paragraphs with escaping, so multi- paragraph replies render correctly.

      Install

      pip install --upgrade connectonion
      

      Breaking Changes

      None.

      Full Changelog : v1.0.9...v1.1.0

    6. πŸ”— openonion/connectonion v1.0.9 release

      Release 1.0.9: co outlook scheduled, reply formatting + scheduling

    7. πŸ”— r/LocalLLaMA Qwen & Gemma on deadlock situation (For Benchmarks Numbers)? rss

      Qwen & Gemma on deadlock situation (For Benchmarks Numbers)? | I have this feeling for sometime. Also noticed few similar tweets online before. submitted by /u/pmttyji
      [link] [comments]
      ---|---

    8. πŸ”— benji.dog rss

      4 kids sitting in the bucket of a cargo bike riding down a street with cars
and other bikes around

      Took the whole crew on a geocaching adventure

    9. πŸ”— openonion/connectonion Release v1.0.8 release

      Highlights

      Scheduled replies : co outlook reply <#> "message" --at +2h. v1.0.7 added scheduled sends; now threaded replies can be deferred the same way β€” Exchange holds the reply until the target time.

      What's Changed

      ✨ Features

      • co outlook reply --at +30m|+2h|<UTC ISO> β€” schedule a threaded reply (#182)
      • Outlook.reply(email_id, body, send_at=...) for agents using the tool directly

      Verified live: scheduled replies were held by Exchange and delivered after their target times.

      Installation

      pip install --upgrade connectonion
      

      Breaking Changes

      None.

      Full Changelog : v1.0.7...v1.0.8

    10. πŸ”— openonion/connectonion Release v1.0.7 release

      Highlights

      co outlook β€” send, receive, reply, and schedule Outlook email straight from the terminal. Attach files and screenshots, pipe in multi-line bodies, and script it safely with real exit codes.

      What's Changed

      ✨ Features

      • New co outlook command group (#179): inbox / read <#> / reply <#> / send / sent / search
        • --attach file.png (repeatable) β€” send screenshots, PDFs, any file (~3MB Graph limit)
        • --at +30m / --at +2h / UTC ISO β€” scheduled delivery via Exchange deferred send (verified live: held by Exchange, delivered at the target time)
        • - as the message reads the body from stdin (co outlook send a@b.com "Report" - < body.txt)
        • Numbered listings: co outlook read 3 reads from your last inbox/search listing; full Graph IDs also work
        • Script-friendly: exit code 1 on failure, plain untruncated ID-bearing output when piped
      • Outlook tool: list_inbox(), list_search(), send(attachments=, send_at=)

      πŸ› Bug Fixes

      • Graph-safe unread filter (Exchange tenants rejected the old $filter + $orderby combo)
      • sendMail 202 empty-body responses no longer crash
      • --at rejects naive local times (Exchange reads the deferred time as UTC)

      πŸ”§ Other Changes

      • Agent discovery moved to /api/agents on oo-api (#175)
      • image_result_formatter uploads screenshots to oo-api as URLs instead of base64 (#172)
      • read_file vision-flow docs show the oo-api upload step (#181)

      Installation

      pip install --upgrade connectonion
      

      Breaking Changes

      None.

      Full Changelog : v1.0.6...v1.0.7

    11. πŸ”— r/LocalLLaMA New open model from Tencent Hy: Hy3 (295B total 21B active - apache 2.0) rss

      New open model from Tencent Hy: Hy3 (295B total 21B active - apache 2.0) | Collection: https://huggingface.co/collections/tencent/hy3 From elie on 𝕏: https://x.com/eliebakouch/status/2074011171661701466 edit: To clarify: this is the non-preview version of Hy3 and they changed their license from the community one (restrictive + not allowed in SK, UK, EU) to Apache 2.0 submitted by /u/Nunki08
      [link] [comments]
      ---|---

    12. πŸ”— mwemuorg/mwemu map files release

      These are the maps for 32bits and 64bits.
      if you are using the library from crates.io it needs these maps.

      Better clone repo and get maps from there.

      The Makefile is prepared to download this test.zip and perform cargo tests just doing make tests.

    13. πŸ”— r/LocalLLaMA If trends hold, Mythos-class capability may be running on high-end consumer hardware within ~2 years rss

      If trends hold, Mythos-class capability may be running on high-end consumer hardware within ~2 years | submitted by /u/PetersOdyssey
      [link] [comments]
      ---|---

    14. πŸ”— exe.dev Building Software From My Phone rss

      My little town of San Anselmo has a delightful live music concert series in the summer. What they don’t have is a website that is easy to read on my phone.

      I was on vacation when this summer’s schedule posted–and I had intentionally left my laptop at home.

      My first instinct was to ask ChatGPT to paw through and organize a schedule to my tastes, but recently I’ve noticed that farming out such tasks to Pro is lossy. I’d say it lacks serendipity, but it’s more than that. My preferences may appear superficial (bluegrass, please), but in reality, none of us are quite so reducible. Our opinions are complex and only coarsely approximated by what we might think to tell an agent.

      So instead, I pulled out my phone, opened the exe.dev iOS app, created a new VM, and said to Shelley:

      https://www.liveontheavenue.org please organize and analyze all of the events, movies, and bands. Add times and genres to each. Turn the whole thing into a simple, compact calendar that I can browse and scroll through on a single page. Use subagents to confirm band genres. Add a genre filter at the top, maybe a dropdown with checkboxes? Mobile-friendly.

      A few minutes later I had https://live-on-the-avenue.exe.xyz/ up and running. And it turns out the band I’m most excited for is listed under ska and klezmer.

      Sow on the Go

      The curse of having an interesting job is that you think about it all the time. I used to send myself a dozen emails a day with ideas and tasks and bug reports. Each of these was a seed for some work, to be completed later at a computer. Now that my development environment lives on exe.dev VMs, I plant these on the go.

      Because I hate typing on my phone, I added high-quality transcription to the exe.dev iOS app. In addition to being more pleasant than typing, it’s also faster. And, because I’m talking, I naturally provide more content and context, which lends itself to better results.

      For many tasks, reaping and/or weeping (reviewing, testing, deciding what’s actually good) still ultimately requires a laptop. But kicking off work no longer means routing it through tedious, intermediate steps.

      Voice Triage

      At any given point, some of my agents are working, some are blocked on trivial stuff, and some need my serious, sustained attention.

      I can now burn through the trivial stuff rapidly from my phone while taking a walk, getting some sunlight, or tidying the house. I put in my headphones, start up real time voice mode, and work through outstanding Shelley conversations: respond, archive, spin up a new thread, leave for later, repeat. It’s less efficient than staring at my phone, but far more pleasant, and worth the change of pace.

      I’ll write more about voice mode in a future post.

      Share Sheets

      exe.dev has been available on mobile web for ages. (OK, fine, months. Feels like ages.) There were really two key features that drove us to start an iOS app, features that we simply could not ship in the browser. The first was notifications; the second was the share sheet.

      The share sheet lets you send screenshots, files, videos, and more to Shelley on an exe.dev VM. Sending a screenshot of a visual bug is so, so much better than describing it.

      My personal favorite use of the share sheet is for transcriptions.

      When I have a substantial new feature I want to design or a blog post I want to draft, I open up Voice Memos on iOS and talk it out, often for the better part of an hour. I have a VM configured with a Deepgram and a Voxtral integration, and a rich agents.md that teaches it how to do transcriptions. (TL;DR: Wait for a file to appear, fan out text-to-speech to both providers, merge the results, clean up disfluencies, recognize common homophones of β€œexe.dev” and β€œVM”, trim redundant content, and email me the results, all while preserving word choice and structure.) Once I’m done recording, it’s a few taps–and zero typing–to dispatch the audio file to my transcription exe.dev VM; the result lands later in my inbox.

      As with so many things I use exe.dev for, there are third- party services that do much of this, but they do it clumsily, or in a way that requires me to do a lot of pre- or post-work, or in a way that doesn’t integrate well with the rest of my cobbled-together digital life. Making it trivial to share stuff into an exe.dev VM has been a major unlock.

  3. July 05, 2026
    1. πŸ”— IDA Plugin Updates IDA Plugin Updates on 2026-07-05 rss

      IDA Plugin Updates on 2026-07-05

      Activity:

    2. πŸ”— HexRaysSA/plugin-repository commits Update known-repositories.txt rss
      Update known-repositories.txt
      
    3. πŸ”— r/LocalLLaMA I developed a 270 million parameter language model entirely from scratch as an independent research project rss

      I developed a 270 million parameter language model entirely from scratch as an independent research project | The model is built on a custom Transformer architecture featuring Rotary Positional Embeddings, RMSNorm, SwiGLU feed forward layers, grouped query attention, and an efficient autoregressive decoder optimized for local inference. Here is the Huggingface Spaces Demo link - https://huggingface.co/spaces/pranavupadhyaya52/WikiSmartBot For anyone interested in the pretraining notebook, I've shared the link here - https://colab.research.google.com/drive/1cxRLxUPX_mT4nst-0xGdhctEdqdIlMDb?usp=sharing submitted by /u/ConfectionAfter2366
      [link] [comments]
      ---|---

    4. πŸ”— backnotprop/plannotator v0.22.0 release

      Follow @plannotator on X for updates

      Missed recent releases? Release | Highlights
      ---|---
      v0.21.4 | Markdown math rendering, PR Overview panel with annotatable description and comments, agent instructions in code review, media parsing fixes
      v0.21.3 | File comments in code review, unified click-to-highlight comments, VS Code clipboard/keyboard bridge, Codex Ask AI on app-server transport, CLI subcommand help
      v0.21.2 | Custom reviews as Agent Skills, Cursor + OpenCode review engines, whole-file/general findings, deleted-annotation fix, Codex Ask AI outside git repos
      v0.21.1 | Annotate-last blank-page fix on multi-message sessions
      v0.21.0 | Direct document editing in annotate mode, live git-status file tree, in-app agent terminal, open files in external apps, HTML renders as HTML
      v0.20.3 | Annotations no longer lost when clicking away, off-screen indicator for open comments
      v0.20.2 | Pierre CodeView all-files review, large-PR pipeline and instant-open checkout, unified agent engine selection, Pi programmatic plan mode
      v0.20.1 | Pi extension install hotfix (pinned @pierre/diffs after a broken upstream release)
      v0.20.0 | Multi-repo workspace reviews, semantic diff overview, UI 2.0 themes and plan look chooser, leaner single-source skill install
      v0.19.27 | Kiro CLI integration, Glimpse native window, annotate-last message picker
      v0.19.26 | Amp plugin production fixes, Mermaid rendering fix, Settings flicker fix, update notification toast and shimmer


      What's New in v0.22.0

      This release rebuilds the code review left panel around how a review actually starts: a git-status view of everything since your base branch is now the default, a Commits panel gives you the branch's history with per-commit diffs, and Guided Review turns any changeset into an agent-organized, chaptered walkthrough with live annotatable diffs. Pi and GitHub Copilot CLI join Cursor and OpenCode as review engines. Five PRs and four direct fixes land, all from the core team, verified by a 24-point QA pass before tagging.

      The review now opens on "All changes"

      Code reviews used to open on unstaged changes, which answers "what did I just edit" but not the question most reviews start with: "what would a PR show if I pushed right now?" The new default diff answers exactly that. All changes compares the merge base with your base branch against the working tree and includes untracked files β€” committed work, uncommitted edits, and brand-new files in one view.

      It renders as a Git status panel with three sections that mirror git status: Committed , Changes , and Untracked. Each row carries viewed tracking, a stage/unstage button, the change-type letter, and +/- counts, so you can stage files as you review without leaving the app. If the base branch has moved on GitHub since your last fetch, a banner offers a one- click fetch so you're reviewing against the real base.

      A first-run dialog lets you pick your default view and diff type (with live previews), and both remain changeable in Settings β†’ Git or from the review header menu. On repos where a base branch can't be resolved, the review falls back to uncommitted changes rather than hiding committed work.

      PR #990, by @backnotprop.

      Commits panel

      The panel toggle gained a third view: Commits , a linear history rail of your branch, newest first, with author avatars and an "In origin/main" divider where your work meets the base. Clicking a commit opens that commit's own diff against its parent β€” git show, but annotatable β€” headed by the full commit message rendered as markdown.

      The toggle is session-scoped: glancing at Commits (or Tree) mid-review never silently changes your saved default, and a review always opens on files, never on a historical commit. Avatars resolve by author email through the repo's forge and fall back to initials when there's no remote or CLI to ask.

      PR #994, by @backnotprop.

      Guided Review

      Large changesets are hard to review top-to-bottom in file order. A Guided Review has an agent organize the current changeset β€” any PR or local diff β€” into importance-ordered chapters: the heart of the change first, its consequences next, glue last. Each section pairs a prose overview and per-file summaries with the live diffs it covers, and those diffs are the real diff viewer β€” annotations made inside a guide land in the same review state and export in the same feedback as everywhere else.

      Open it with the Guide button in the review header or Mod+Shift+G, pick an engine and model, and generate. Sections track their own reviewed state so you can work through a big change across sittings. Guides run on Claude or Codex natively, and on Cursor, OpenCode, Pi, or GitHub Copilot CLI when installed. Every changed file is validated against the real diff server-side, so a guide can never invent files or drop them silently.

      A one-time intro dialog announces the feature on first open, and the Guide button carries a subtle hint until the first time you use it.

      PRs #993, #997, and #1000, by @backnotprop.

      Pi and GitHub Copilot CLI as review engines

      The review and guide launchers gained two engines. Pi rides your existing Pi login (OAuth subscription or keys) with live model discovery and thinking- level control β€” and the Pi extension can launch Pi, so Pi users get agent reviews with zero extra setup. GitHub Copilot CLI runs in a locked-down non-interactive posture: no write access, a shell allowlist limited to git- family commands, and clean auto-denial for anything else.

      That brings the engine roster to Claude, Codex, Cursor, OpenCode, Pi, and Copilot β€” and the engine layer was refactored so the next one is a two-edit change.

      PRs #993 and #997, by @backnotprop.

      Additional Changes

      • Filenames stay visible in the Git status panel β€” long paths now truncate in the directory portion (ellipsis before the final slash) so the filename always shows; a pathologically long filename truncates at its own end.
      • Commit-diff annotations stay anchored to their commit β€” annotations made on a historical commit's diff are stamped with that commit, and the exported feedback labels any annotation whose anchor doesn't match the diff being sent, so an agent never reads a commit's line numbers against the working tree.
      • Avatar lookups can't delay the commit list β€” forge avatar resolution now has a hard 4-second ceiling; on a hanging network the Commits panel loads immediately with initials and picks up avatars once they arrive.
      • Code review reference docs updated β€” the docs page now describes the since-base default, the three panel views, Guided Review, the full engine roster, and the current server API.

      Install / Update

      macOS / Linux:

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

      Windows:

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

      Extra skills (compound, setup-goal, visual-explainer), opt-in:

      npx skills add backnotprop/plannotator/apps/skills/extra
      

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

      OpenCode: Clear cache and restart:

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

      Then in opencode.json:

      {
        "plugin": ["@plannotator/opencode@latest"]
      }
      

      Pi: Install or update the extension:

      pi install npm:@plannotator/pi-extension
      

      Droid: Install via the plugin marketplace:

      droid plugin marketplace add backnotprop/plannotator
      droid plugin install plannotator@plannotator
      

      Amp: Install the CLI first, then copy the plugin:

      mkdir -p ~/.config/amp/plugins
      curl -fsSL https://raw.githubusercontent.com/backnotprop/plannotator/main/apps/amp-plugin/plannotator.ts \
        -o ~/.config/amp/plugins/plannotator.ts
      

      Kiro CLI: The installer auto-detects Kiro and installs skills automatically. After installing the CLI, launch with:

      kiro-cli chat --agent plannotator
      

      Upgrading from before v0.20.0? Read the v0.20.0 release notes first; that release changed how skills install.


      What's Changed

      • feat(review): "All changes" git-status review view by @backnotprop in #990
      • feat(review): Commits panel β€” linear history rail with per-commit diffs by @backnotprop in #994
      • feat(review): Guided Review + Pi agent-job provider by @backnotprop in #993
      • feat: guide per-file summaries + GitHub Copilot CLI agent engine by @backnotprop in #997
      • feat(review): Guided Reviews intro dialog + Guide button discovery hint by @backnotprop in #1000
      • fix(review): keep the filename visible in git-status rows by @backnotprop
      • fix(review): anchor commit-diff annotations to their commit in feedback by @backnotprop
      • fix(review): avatar lookups can no longer delay /api/commits by @backnotprop
      • docs(marketing): bring the code-review reference up to the since-base era by @backnotprop

      Full Changelog : v0.21.4...v0.22.0

    5. πŸ”— r/LocalLLaMA longcat 2.0 (1.6T, ~48B active) weights are now open under MIT license rss
    6. πŸ”— r/LocalLLaMA I benchmarked 13 models at 65K-128K context to find out what actually matters for agentic workloads rss

      I benchmarked 13 models at 65K-128K context to find out what actually

      matters for agentic workloads β€” prefill dominates everything, and KV head count beats parameter count

      I've been running local LLMs for agentic workflows (tool use, coding agents, RAG) and kept seeing people obsess over tg128 (token generation speed) as the headline performance metric. So I ran a structured long-context benchmark to figure out what actually matters when your context window is full. The answer surprised me.


      Setup

      • GPU : RX 7900 XT 20GB (Vulkan backend, RADV/Mesa)
      • Backend : llama.cpp / llama-bench (build 9860)
      • Flags : -ngl 99 (GTT spill), -fa on, -ub 2048 -b 16384, ASPM=performance, bare TTY to free VRAM
      • 13 models : 5 dense, 6 MoE, 1 Mamba2 hybrid, 1 MLA MoE β€” ranging from 5GB to 18GB
      • 3 KV cache tiers : Q8_0 K / Q4_0 V (aggressive), Q8_0 K / Q8_0 V (symmetric), F16 (baseline)
      • Context sizes : 512, 4K, 16K, 65K, 131K β€” both pure prefill (pp) and prompt+gen (pg)
      • Full run took ~21 hours across two sessions

      Full prefill speed results (Q8_0 K / Q8_0 V KV cache, tokens/sec)

      If you just want the raw numbers, here's every model tested. pp = pure prompt processing (prefill), tg128 = token generation (decode). Sorted by pp131K.

      Model | Size | Type | pp512 | pp4K | pp16K | pp65K | pp131K | tg128
      ---|---|---|---|---|---|---|---|---
      Trinity-Mini | 16G | MoE 3B/26B | 2639 | 2924 | 2370 | 1419 | 923 | 150
      Granite-4.0-H-Small | 17G | Mamba2+MoE | 1115 | 1271 | 1220 | 1043 | 875 | 71
      Ornith-9B / Qwen3.5-9B | 6G | Dense | 2103 | 2220 | 1943 | 1274 | 873 | 92
      Qwen3.6-35B-A3B | 18G | MoE 3B/35B | 2184 | 2736 | 2227 | 1268 | 802 | 110
      Gemma-4-26B-A4B | 14G | MoE 4B/26B | 2523 | 2798 | 2076 | 1024 | 600 | 119
      North-Mini-Code | 15G | MoE 3B/30B | 2155 | 2187 | 1568 | 900 | 579 | 134
      Gemma-4-12B | 7G | Dense | 1492 | 1498 | 1145 | 595 | 350 | 66
      Qwen3.6-27B | 16G | Dense | 693 | 681 | 602 | 406 | 285 | 32
      Granite-4.1-8B | 5G | Dense | 1965 | 1807 | 1124 | 442 | 244 | 93
      Ministral-3-14B | 8G | Dense | 1419 | 1325 | 916 | 404 | 232 | 67
      Apriel-1.6-15B | 9G | Dense | 1332 | 1208 | 812 | 347 | 197 | 66
      Devstral-24B | 15G | Dense | 829 | 796 | 628 | 313 | --- | 42
      GLM-4.7-Flash | 16G | MoE (MLA) | 1822 | 1054 | 358 | --- | --- | ---

      A few things to note: Devstral-24B couldn't complete the 131K test (8 KV heads Γ— 128 dim = 160 KB/token β€” KV cache alone is ~21GB at 131K). GLM-4.7-Flash crashed above 16K (MLA issue, see Finding 5). Ornith-9B is architecturally identical to Qwen3.5-9B.


      Finding 1: At 65K+ context, prefill is 94–99% of wall-clock time. tg128 is nearly irrelevant for short agentic outputs.

      Here's the wall-clock breakdown for a real agentic query β€” 65K context in, 300 tokens out (typical tool-use response). Sorted by total time:

      Model | Type | Prefill | Decode | Total | Prefill %
      ---|---|---|---|---|---
      Trinity-Mini (MoE 3B/26B) | MoE | 46.2s | 2.0s | 48.2s | 96%
      Qwen3.6-35B-A3B (MoE) | MoE | 51.7s | 2.7s | 54.4s | 95%
      Ornith-9B / Qwen3.5-9B | Dense | 51.4s | 3.3s | 54.7s | 94%
      Gemma-4-26B-A4B (MoE) | MoE | 64.0s | 2.5s | 66.5s | 96%
      Granite-4.0-H-Small (Mamba2) | Mamba2 | 62.8s | 4.2s | 67.1s | 94%
      North-Mini-Code (MoE) | MoE | 72.8s | 2.2s | 75.0s | 97%
      Gemma-4-12B | Dense | 110.2s | 4.5s | 114.7s | 96%
      Granite-4.1-8B | Dense | 148.4s | 3.2s | 151.6s | 98%
      Qwen3.6-27B | Dense | 161.4s | 9.3s | 170.7s | 95%
      Ministral-3-14B | Dense | 162.0s | 4.5s | 166.5s | 97%
      Apriel-1.6-15B | Dense | 188.9s | 4.6s | 193.5s | 98%
      Devstral-24B | Dense | 209.5s | 7.2s | 216.6s | 97%

      Decode is 1–5% of the time you actually wait. If your agent makes a short tool call or writes a brief response, the only thing that matters is how fast you can process the context window.

      This means benchmark reports that lead with tg128 are misleading for agentic use cases. pp65K / pp131K is the metric that matters. The pg(prompt, gen) blended metric is better but still obscures the split β€” a model with fast prefill + catastrophically slow decode can look mediocre on pg despite being excellent for short outputs.


      Finding 2: KV head count is the dominant architectural factor for long- context prefill β€” not parameter count, not MoE vs dense

      Prefill speed retention (% of pp4K speed) at increasing context, all models:

      Model | Size | KV Heads | pp4K | 16K | 65K | 131K | Type
      ---|---|---|---|---|---|---|---
      Granite-4.0-H-Small | 17G | Mamba2 | 1271 | 96% | 82% | 69% | Mamba2+MoE
      Qwen3.6-27B | 16G | 4Γ—256 | 681 | 88% | 60% | 42% | Dense
      Ornith-9B / Qwen3.5-9B | 6G | 4Γ—128 | 2220 | 87% | 57% | 39% | Dense
      Trinity-Mini | 16G | 4Γ—128 | 2924 | 81% | 49% | 32% | MoE
      Qwen3.6-35B-A3B | 18G | 4Γ—128 | 2736 | 81% | 46% | 29% | MoE
      Gemma-4-12B | 7G | 8Γ—128 | 1498 | 76% | 40% | 23% | Dense
      Gemma-4-26B-A4B | 14G | 4Γ—256 | 2798 | 74% | 37% | 21% | MoE
      North-Mini-Code | 15G | 4Γ—128 | 2187 | 72% | 41% | 26% | MoE
      Apriel-1.6-15B | 9G | 8Γ—128 | 1208 | 67% | 29% | 16% | Dense
      Ministral-3-14B | 8G | 8Γ—128 | 1325 | 69% | 31% | 18% | Dense
      Granite-4.1-8B | 5G | 8Γ—128 | 1807 | 62% | 24% |
      14% | Dense
      Devstral-24B | 15G | 8Γ—128 | 796 | 79% | 39% | --- | Dense
      GLM-4.7-Flash | 16G | MLA (1Γ—576) | 1054 |
      34%* | --- | --- | MoE (MLA)

      __ Granite-H-Small has 4 attention layers + 36 Mamba2 layers (recurrent state, no KV cache)*

      Ornith-9B / Qwen3.5-9B (9B dense, 4 KV heads Γ— 128 dim = 64 KB/token KV) is 4.4Γ— faster at 128K context than Apriel-15B (15B dense, 8 KV heads Γ— 128 dim = 160 KB/token) β€” despite being the same dense class and half the size. The difference is purely KV cache architecture. Every attention pass has to scan the full KV cache, and 8 KV heads means 2.5Γ— more data to scan per token.

      Practical rule : When evaluating a model for long context, check n_kv_heads and head_dim in the config before looking at parameter count. Two models from the same family can differ by 3–4Γ— at 128K if one has 4 KV heads and the other has 8.


      Finding 3: Mamba2 hybrid models have near-flat prefill scaling. The architecture actually delivers.

      I was skeptical of the Mamba2 hype, but the data is clear. Granite-4.0-H-Small (IBM, 4 attention layers + 36 Mamba2 layers) retains 69% of its pp4K speed at 131K context β€” every transformer model in the test dropped below 42%.

      Model | pp4K | pp131K | Slowdown
      ---|---|---|---
      Granite-H-Small (Mamba2) | 1271 | 875 | 1.45Γ—
      Trinity-Mini (MoE) | 2924 | 923 | 3.2Γ—
      Ornith-9B / Qwen3.5-9B (Dense GQA) | 2220 | 873 | 2.5Γ—
      Granite-8B (Dense) | 1807 | 244 | 7.4Γ—

      At 131K context, Granite-H-Small (17GB) ties Ornith-9B / Qwen3.5-9B (6GB) at ~875 t/s despite being 3Γ— the file size. The Mamba2 layers use fixed recurrent state instead of growing KV cache, so only 4 attention layers contribute to KV growth.

      The catch: its decode is slow (71 t/s) and its reasoning quality is low. But for the specific workload pattern of "huge context, short output" β€” which is exactly what agentic tool use looks like β€” the prefill scaling advantage is real and measurable. If someone trains a good model on this architecture, it could be a serious agentic contender.


      Finding 4: F16 KV cache can be FASTER than Q8/Q4 quantized KV β€” the dequantization paradox

      Conventional wisdom says quantize your KV cache (Q8_0 K / Q4_0 V) for speed β€” smaller cache, less bandwidth. I tested this head-to-head at 65K context, comparing F16 baseline against Q8_0 K / Q8_0 V (the results were identical to Q8K/Q4V within Β±1% β€” V cache quantization choice turned out to be irrelevant):

      Model | Type | Q8K/Q8V | F16 | F16 advantage
      ---|---|---|---|---
      Gemma-4-26B-A4B | MoE | 1015 | 1554 | +53%
      Gemma-4-12B | Dense (7GB) | 593 | 857 | +44%
      Qwen3.6-35B-A3B | MoE | 1273 | 1573 | +24%
      Ornith-9B / Qwen3.5-9B | Dense (6GB) | 1276 | 1544 | +21%
      Trinity-Mini | MoE | 1429 | 1583 | +11%
      Granite-H-Small | Mamba2 | 1040 | 984 | -5%
      Ministral-3-14B | Dense (8KV) | 409 | 335 | -18%
      Granite-4.1-8B | Dense (8KV) | 447 | 358 | -20%
      Apriel-1.6-15B | Dense (8KV) | 351 | 120 | -66%

      F16 wins for MoE models and small dense models. It loses badly for dense models with many KV heads. Here's why:

      The Q8/Q4 KV cache dequantization is a compute operation that scales with context length. At 65K context, the attention kernel has to dequantize 65K Γ— n_kv_heads Γ— head_dim Q8/Q4 elements per token. This compute cost exceeds the bandwidth saved by halving the cache size.

      Meanwhile, F16 KV doubles the cache but requires zero dequantization. For MoE models, the larger F16 cache causes GTT spill β€” but only the active parameters (~3B of 35B) cross PCIe, so the spill penalty is small. For dense models with 8 KV heads, the F16 cache is both larger and all weights spill β€” double penalty.

      Updated rule:

      • F16 wins : MoE models (tiny active spill footprint), small dense models (<10GB), efficient-GQA dense (4 KV heads)
      • F16 loses : Dense + 8+ KV heads + >10GB (full weight spill + large KV = catastrophic)
      • Q8K/Q4V vs Q8K/Q8V : Complete wash (Β±1%) across every model. V cache quantization choice is irrelevant β€” pick whichever.

      Test F16 on your hardware at your actual working context. The conventional wisdom isn't always right.


      Finding 5: MLA (Multi-head Latent Attention) degrades hard on Vulkan as context grows

      GLM-4.7-Flash (1 KV head, 576 dim β€” MLA architecture) showed a steep prefill degradation:

      Context | pp (t/s) | vs pp512
      ---|---|---
      pp512 | 1822 | 100%
      pp4K | 1054 | 58%
      pp16K | 358 | 20%
      pp65K | crashed | ---

      That's an 80% drop from 512 to 16K context. The 65K benchmark test crashed (Vulkan DeviceLost). However β€” I want to be clear about what this data does and doesn't show. I didn't test any context sizes between 16K and 65K, so the exact crash boundary is unknown. I'm also running this same model in daily use at 20K+ context without crashes, so it's not a hard wall at 16K β€” the failure point is somewhere above that.

      What is clear from the data: MLA's prefill scaling on Vulkan is dramatically worse than standard attention. Whether that becomes a hard crash or just very slow depends on context size and VRAM headroom. The 66% drop from pp4K to pp16K is measured and real β€” MLA's KV compression/decompression kernel appears to scale poorly with context on the Vulkan backend. Don't extrapolate short- context MLA benchmarks to long context on Vulkan, and don't assume the degradation is linear β€” it looks like it accelerates. This may be backend- specific β€” CUDA or Metal may handle MLA's attention pattern better.


      Finding 6: MoE models win the speed Γ— intelligence composite for agentic work

      Combining the speed data with Artificial Analysis Intelligence Index v4.1 scores into a composite (intelligence weighted by inverse wall-clock time). Score = AA Intel Γ— (50s / wall_clock_at_65K), so 50s wall = 1.0Γ— AA multiplier.

      Rank | Model | AA Intel | Size | Wall (65K) | Type | Composite
      ---|---|---|---|---|---|---
      1 | Qwen3.6-35B-A3B | 32 | 18G | 54s | MoE | 29.4
      2 | Trinity-Mini | 24 | 16G | 48s | MoE | 24.9
      3 | Gemma-4-26B-A4B | 26 | 14G | 67s | MoE | 19.6
      4 | North-Mini-Code | 21 | 15G | 75s | MoE | 14.0
      5 | Ornith-9B / Qwen3.5-9B | 15 | 6G | 55s | Dense | 13.7
      6 | Qwen3.6-27B | 37 | 16G | 171s | Dense | 10.8
      7 | Gemma-4-12B | 18 | 7G | 115s | Dense | 7.8
      8 | Granite-4.0-H-Small | 7 | 17G | 67s | Mamba2 | 5.2
      9 | Apriel-1.6-15B | 19 | 9G | 194s | Dense | 4.9
      10 | Ministral-3-14B | 15 | 8G | 167s | Dense | 4.5
      11 | Granite-4.1-8B | 12 | 5G | 152s | Dense | 4.0
      12 | Devstral-24B | 17 | 15G | 217s | Dense | 3.9

      GLM-4.7-Flash excluded β€” no 65K data (MLA crash).

      The smartest model (Qwen3.6-27B, AA=37) finishes 6th because it takes 3Γ— longer than the MoE models. The top MoE (Qwen3.6-35B-A3B, AA=32) delivers 87% of the intelligence at 3Γ— the speed. For agentic workflows where the model makes multiple short-output iterations, that tradeoff wins.

      MoE models benefit twice: fast decode (read only active params) and cheap GTT spill (only active weights cross PCIe when context forces spillover). The combination makes them disproportionately strong at long context on constrained VRAM.


      TL;DR β€” Practical takeaways for local agentic LLM deployment

      1. Stop obsessing over tg128. At 65K+ context, prefill is 94–99% of wall-clock for short outputs. Benchmark pp65K / pp131K instead.
      2. Check KV head count first. 4 KV heads Γ— 128 dim (64 KB/token) scales dramatically better than 8 KV heads Γ— 128 dim (160 KB/token) at long context β€” regardless of model size.
      3. Test F16 KV cache at your working context. It can be 20–53% faster than Q8/Q4 for MoE and small dense models. The conventional "always quantize KV" wisdom breaks at long context on some architectures.
      4. MoE models are the sweet spot for agentic long-context on consumer VRAM. Cheap GTT spill + fast decode + competitive intelligence. A 30B-class MoE delivers 80%+ of top dense model intelligence at 2–3Γ— the speed.
      5. Mamba2 hybrids are real. Near-flat prefill scaling to 131K. Currently let down by training quality, not architecture. Worth watching.
      6. MLA degrades hard on Vulkan as context grows. GLM-4.7-Flash lost 80% of prefill speed from 512 to 16K context and crashed at 65K. It's usable at moderate context (I run it daily at 20K+), but the scaling curve is steep and non-linear. Don't extrapolate short-context MLA benchmarks.

      All data collected on llama.cpp build 9860, Vulkan backend, RX 7900 XT 20GB. Your mileage will vary on other hardware β€” especially the F16 KV findings, which depend on your GPU's bandwidth vs compute ratio and your VRAM headroom. The architectural patterns (KV heads matter, prefill dominates, MoE scales better) should generalize.

      Happy to share the raw JSONL or the benchmark script if anyone wants to reproduce.

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

    7. πŸ”— HexRaysSA/plugin-repository commits sync repo: +1 plugin, +1 release rss
      sync repo: +1 plugin, +1 release
      
      ## New plugins
      - [IDA-Discord-RPC](https://github.com/reversedcodes/ida-rpc) (1.1.0)
      
    8. πŸ”— Simon Willison sqlite-utils 4.0rc2, mostly written by Claude Fable (for about $149.25) rss

      I wrote about the sqlite-utils 4.0rc1 release a couple of weeks ago. Since we only have Claude Fable on our Max subscriptions for a few more days, I decided to see if it could help me get to a 4.0 stable release that I felt truly comfortable about, since I try to keep to SemVer and like my incompatible major versions to be as rare as possible.

      I started with this prompt, in Claude Code for web on my iPhone:

      Final review before shipping a stable 4.0 release - very important to spot any last minute things that would be a breaking change if we fix them later

      Here's that initial report it created for me. There were some significant problems that I hadn't myself encountered yet - 5 that Fable categorized as "release blockers". Here's the worst of the bunch:

      1. delete_where() never commits and poisons the connection (data loss)

      Table.delete_where() (sqlite_utils/db.py:2948) runs its DELETE via a bare self.db.execute() with no atomic() wrapper β€” compare Table.delete() at db.py:2944, which wraps correctly. The connection is left in_transaction=True, so every subsequent atomic() call takes the savepoint branch (db.py:430-440) and never commits either.

      Reproduced end-to-end:

      db = sqlite_utils.Database("dw.db")
      db["t"].insert_all([{"id": i} for i in range(3)], pk="id")
      db["t"].delete_where("id = ?", [0])   # conn.in_transaction is now True
      db["t"].insert({"id": 50})
      db["u"].insert({"a": 1})
      db.close()
      # Reopen: rows are [0, 1, 2] β€” the delete, row 50, AND table u are all gone.

      That's a really bad bug! Very glad I didn't ship that, although at least it would have been a bug I could fix in a 4.0.1 point release, not a design flaw that would force a 5.0.

      Over the course of 37 prompts, 34 commits and +1,321 -190 code changes over 30 separate files, we worked through the entire set of feedback in turn, making several other design improvements along the way.

      A weird thing about coding agents is that harder tasks like this one actually provide more opportunity to do other things at the same time, since the agent sometimes needs 10-15 minutes to churn away on a new task. I went out to enjoy the Half Moon Bay 4th of July parade, occasionally checking in and prompting the next step for Fable from my phone.

      Full details in the PR and this shared transcript. I switched to my laptop for the final review, which I conducted through GitHub's PR interface.

      The most significant changes relate to transaction handling, which was the signature new feature in the earlier RC. The new RC now includes comprehensive documentation on the new transaction model, the intro to which I'll quote here in full:

      Every method in this library that writes to the database - insert(), upsert(), update(), delete(), delete_where(), transform(), create_table(), create_index(), enable_fts() and the rest - runs inside its own transaction and commits it before returning. Your changes are saved to disk as soon as the method call finishes:

      db = Database("data.db")
      db.table("news").insert({"headline": "Dog wins award"})
      # The new row is already saved - no commit() required

      The same applies to raw SQL executed with db.execute() - a write statement is committed as soon as it has run.

      You never need to call commit(), and you do not need to close the database to persist your changes. There are exactly two situations where you need to think about transactions:

      1. You want to group several write operations together, so they either all succeed or all fail - use db.atomic().

      2. You are managing a transaction yourself with db.begin(), in which case nothing is committed until you commit - the library will never commit a transaction you opened.

      In reviewing Fable's documentation - I find that reviewing the documentation edits first is an excellent way to build an initial understanding of what has changed - I spotted this detail:

      db.atomic() and the automatic per-method transactions are designed for connections in Python's default transaction handling mode. Connections created with the Python 3.12+ sqlite3.connect(..., autocommit=True) or autocommit=False options are not supported, because commit() and rollback() behave differently on those connections.

      I admit I hadn't thought about how sqlite-utils would react to the more recent autocommit setting, added in Python 3.12. It turns out "behave differently on those connections" equated to almost the entire test suite failing, so I worked with the model to ensure that this difference would not break how the library works.

      And a final review by GPT-5.5

      I used to think that the idea of having one model review the work of another was somewhat absurd - it felt weirdly superstitious. The problem is it really does work - I've started habitually having Anthropic's best model review OpenAI's work and vice versa, because I've had that turn up interesting results often enough to be valuable.

      I prompted Codex Desktop and GPT-5.5 xhigh with the following:

      Review changes since the last RC. Also confirm that the changelog is up-to-date.

      Which was enough to turn up two issues worth investigating:

      Findings

      • [P1] sqlite_utils/db.py:663 db.query() now rejects non-row statements only after calling db.execute(), and sqlite_utils/db.py:705 auto-commits those writes first. So db.query("update ...") raises ValueError but the update is already committed. That is a surprising side effect for a method documented as β€œcan only be used with SQL that returns rows.”
      • [P1] sqlite_utils/db.py:672 INSERT ... RETURNING through db.query() only commits after the returned generator is fully exhausted. db.query("insert ... returning ...") without iteration, or common next(db.query(...)) usage, leaves the transaction open and the write can be rolled back on close. This contradicts docs/changelog.rst:15 and docs/python-api.rst:232, which say it takes effect without iteration.

      I pasted that into a fresh Fable session, which ran some experiments to confirm the problem:

      Both findings were confirmed. db.query() called self.execute() first, which auto-commits writes, and only then checked cursor.description β€” so db.query("update ...") committed the update before raising ValueError. And the INSERT ... RETURNING commit lived at the end of the returned generator, so it never fired unless you exhausted the iterator β€” next(db.query(...)) or an un-iterated call left the transaction open, contradicting what the changelog and docs promise.

      Here's the PR with the fix, and the full Claude Code transcript. Reviewing this code helped me build a better mental model of the edge cases of SQLite transaction semantics!

      For an estimated (unsubsidized) cost of $149.25

      I upgraded to the Claude Max $200/month plan (I was previously on $100/month) to increase my Fable allowance for the remaining time until the July 7th Fablepocalypse, when even Claude Max subscribers will have to pay full API cost for the model.

      I was curious as to how much this would have cost me if I had been paying those costs directly. At first I thought those numbers weren't available to me since I had run the work remotely using Claude Code for web, and then I realized I could run AgentsView inside that existing session to get that cost estimate!

      Run "uvx agentsview --help"​ and then use that tool to calculate the cost of this session

      Claude figured out how to use the session list --include-children command and came out with the following:

      Transcript Model Cost
      Main session claude-fable-5 $141.02
      API-surface sweep agent claude-fable-5 $2.40
      Transactions/atomic review agent claude-fable-5 $2.39
      Post-rc1 commits review agent claude-fable-5 $1.72
      Migrations review agent claude-fable-5 $1.40
      Prompt-counting agent claude-opus-4-8 $0.32
      Total $149.25

      I'm very glad I'm on that subscription! I really should have followed my own advice and leaned more heavily into subagents with cheaper models.

      Here's what claude.ai/settings/usage is showing me right now:

      Screenshot of a Claude plan usage limits panel: "Plan usage limits Max (20x)"; "Current session" with "Resets in 3 hr 52 min" showing a progress bar at "7% used"; "Weekly limits" heading with a "Learn more about usage limits" link; "All models" with "Resets Wed 12:00 PM" showing a progress bar at "32% used"; "Fable" with "Resets Wed 12:00 PM" showing a progress bar at "63% used".

      I have several other major Fable-driven projects on the go right now as well, with the goal of hitting 100% on that Fable bar just in time for the price increase.

      The full release notes for sqlite-utils 4.0rc2

      Here are the full release notes for the RC. I had Fable add these to an "Unreleased" section of the changelog as each change landed, reviewing them as it went. This has the neat side effect that the commit history of the changelog acts as a concise summary of each of the changes that went into the release.

      In the past I've had a policy of writing release notes by hand, but honestly these are better than I would have created myself. Release notes are a great example of writing that I'm OK to outsource to agents because they need to be boring, predictable and accurate.

      Breaking changes:

      • Write statements executed with db.execute() are now committed automatically, unless a transaction is already open in which case they join it. Previously they opened an implicit transaction that stayed open until something committed it - writes appeared to work when read on the same connection but were silently rolled back when the connection closed. Code that relied on rolling back uncommitted db.execute() writes should use the new db.begin() method to open an explicit transaction first. The transaction model is documented in full at Transactions and saving your changes.
      • db.query() now executes its SQL as soon as it is called, rather than waiting until the returned generator is first iterated. Rows are still fetched lazily during iteration. SQL errors are now raised at the call site, statements such as INSERT ... RETURNING are executed and committed immediately without needing to iterate over their results, and passing a statement that returns no rows - previously a silent no-op - now raises a ValueError recommending db.execute() instead. A statement rejected this way is rolled back before the error is raised, so it has no effect on the database.
      • Python API validation errors now raise ValueError instead of AssertionError. Previously invalid arguments - such as create_table() with no columns, transform() on a table that does not exist, or passing both ignore=True and replace=True - were rejected using bare assert statements, which are silently skipped when Python runs with the -O flag. Code that caught AssertionError for these cases should catch ValueError instead.
      • table.upsert() and table.upsert_all() now raise PrimaryKeyRequired if a record is missing a value for any primary key column, or has a value of None for one. Previously such records - which can never match an existing row - were quietly inserted as brand new rows, or triggered a confusing KeyError after the insert had already taken place.
      • db.enable_wal() and db.disable_wal() now raise a sqlite_utils.db.TransactionError if called while a transaction is open. Previously they would silently commit the open transaction as a side effect of changing the journal mode, breaking the rollback guarantee of db.atomic() and of user-managed transactions.
      • The View class no longer has an enable_fts() method. It existed only to raise NotImplementedError, since full-text search is not supported for views - calling it now raises AttributeError instead, and the method no longer appears in the API reference. The sqlite-utils enable-fts command shows a clean error when pointed at a view.
      • The no-op -d/--detect-types flag has been removed from the insert and upsert commands. Type detection has been the default for CSV/TSV data since 4.0a1, so the flag did nothing - invocations using it should simply drop it. --no-detect-types remains available to disable detection.
      • Database() now raises a sqlite_utils.db.TransactionError if passed a connection created with the Python 3.12+ sqlite3.connect(..., autocommit=True) or autocommit=False options. commit() and rollback() behave differently on those connections, which previously caused every write made by the library to be silently discarded when the connection closed.

      Everything else:

      • Fixed a bug where table.delete_where(), table.optimize() and table.rebuild_fts() did not commit their changes, leaving the connection inside an open transaction. Their work - and any subsequent writes - could then be silently rolled back when the connection was closed. All three now use db.atomic(), consistent with the other write methods.
      • The sqlite-utils drop-table command now refuses to drop a view, and drop-view refuses to drop a table. Previously each would silently drop the wrong type of object if the name matched. Both now exit with an error suggesting the correct command to use.
      • Migrations applied by the new migrations system now run inside a transaction, together with the record of the migration having been applied. If a migration raises an exception its changes are rolled back and it stays pending, so it can be safely re-applied after the error is fixed. Migrations that cannot run inside a transaction, such as those executing VACUUM, can opt out using @migrations(transactional=False) - see Migrations and transactions.
      • table.upsert() and table.upsert_all() now detect the primary key or compound primary key of an existing table, so the pk= argument is no longer required when upserting into a table that already has a primary key.
      • db.table(table_name).insert({}) can now be used to insert a row consisting entirely of default values into an existing table, using INSERT INTO ... DEFAULT VALUES. (#759)
      • Improvements to the sqlite-utils migrate command: --stop-before values that do not match any known migration are now an error instead of being silently ignored, --stop-before now works correctly with migration files that still use the older sqlite_migrate.Migrations class, and --list is now a read-only operation that no longer creates the database file or the migrations tracking table. migrations.applied() now returns migrations in the order they were applied.
      • New db.begin(), db.commit() and db.rollback() methods for taking manual control of transactions, as an alternative to the db.atomic() context manager.
      • New documentation: Transactions and saving your changes describes how transactions work and when changes are committed, and a new Upgrading page details the changes needed to move between major versions.

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

  4. July 04, 2026
    1. πŸ”— IDA Plugin Updates IDA Plugin Updates on 2026-07-04 rss

      IDA Plugin Updates on 2026-07-04

      New Releases:

      Activity:

    2. πŸ”— r/LocalLLaMA possible evidence of literal prompt injection by anthropic rss
    3. πŸ”— cloudflare/capnweb capnweb@0.9.1 release

      Patch Changes

      • #195 78744ca Thanks @aleister1102! - Fix nodeHttpBatchRpcResponse leaving the connection open and crashing with
        ERR_HTTP_HEADERS_SENT on non-POST requests. It now returns 405 immediately.
    4. πŸ”— Register Spill Joy & Curiosity #90 rss

      … aaaaand we're back, ladies and gentlemen! That was a nice three week break and I've thought quite a bit about what to do with this newsletter when I come back to it.

      The result: I'm going to double down on having fun with it. I think I've gotten into a bit of a rut, after trying to follow the recipe every week for years.

      I don't know what that means exactly, but hey, you've been warned.

      • We released Agents in Orbs this week. This has been a long time in the making and now it's finally out and people are already starting to say "in an orb" as if it's a common phrase, I love it. I truly believe that remote agent-- excuse me: agents in orbs, will play a big role in the future. Why? One reason is that these models are incredible when thrown into a sandbo-- I'm sorry: an orb. I mean, look at this. There aren't any magic strings being pulled behind the curtain. If there's ffmpeg, a model will find a way. These agents need less and less handholding and that includes the handholding by a bespoke development setup. They're productive in these remote machines. The second reason is that something changes when you can start many agents in many different orbs in parallel. I tried to articulate that in that post up there but based on the conversations I had in response, I think it's something you really have to try for yourself. But I can add that the more I use agents in orbs, the more I believe that thinking of remote agents as "agents that I can remote control on a different machine that's similar to my local machine" is the wrong way to look at it. The fact that the orbs are ephemeral changes what you do and how you do it. Just like switching from a single build server to a build system with VMs changes things. State is no longer an issue. Resources and runtime is no longer an issue. These agents in orbs now look like async functions to me, less like remote controlled agents. Async is the point. I now often end prompts with "… and now run all the tests, fix all the bugs you run into, then push" and then switch to another agent. It's very, very interesting and exciting to try to get them to do more and more in orbs and see how it changes your interaction with agents.

      • I also wrote about how we made our codebase work with agents and how we made it work with agents in orbs so that they can do a full end-to-end runthrough of our core workflow inside an orb and then present a screenshot: Putting an Agent in an Orb.

      • At Amp we want Freedom of Intelligence.

      • The results of the Twenty Ninth International Obfuscated C Code Contest are in and interesting as always. Take a look at the Hacker News comments. They contain some gems. This one here, for example, a comment by the author of the "GameBoy emulator's code [that] also looks like the GameBoy": "I first wrote a full Gameboy emulator in C. It started out at about 6000 non white space characters. I then spent about about 100 hours work trying to get it to fit into the 2503 limit. For a long time I wasn't sure it was going to fit."

      • Long and very good post about Turbopuffer: Inside the fastest-growing Canadian AI startup you've never heard of. More of this! The form already exists, of course: new startup, short profile of founders, how they grew, how they blew up, etc. But this feels more nuanced and deeper. Good stuff.

      • Ethan Mollick on working with Mythos: "Last year I called this working with a wizard: you chant the spell and something happens. With Fable the spell has gotten powerful enough that I am no longer sure I am the wizard. I am closer to a patron. I describe what I want, I pay for it, and I judge the result. The conjuring happens somewhere I cannot watch, in hundreds of small choices I never get a vote on. The work has shifted from process to outcome. I no longer steer; I commission."

      • Very thorough post on agents and how models have been trained to be agents: Agents need Work Data. Yours truly makes an appearance in a quote.

      • The End of Determinism. I like the phrase The End of Determinism.

      • Definitely not in the Joy column, but it's a good post: LLMs are eroding my software engineering career and I do not know what to do.

      • 19min talk by Tyler Cowen that neatly summarizes a lot of things I've heard him say or write in other forms: AI will improve our economy, but will we let it?

      • Tim Ferriss on whether AI has already killed how-to nonfiction: "My position--and I'd genuinely love to be wrong--is yes, prescriptive nonfiction is the canary in the coal mine, and the coal mine is enormous. I believe LLMs become the interface to everything: search and purchasing, obviously, but also surfing video, summarizing podcasts, navigating courses, even browsing books. The original content doesn't exactly disappear; it just becomes raw material that most people never touch directly." But the post is deeper than this quote might suggest. Highly recommend reading it. It's great.

      • Very, very, very, very good: Why I Stopped Arguing With People. Read this and if you find it even slightly interesting or even irritating, I recommend reading How to Win Friends and Influence People. Being right isn't valuable most of the time.

      • Giant Banana Pulled Over in Montana. I wish I had a giant banana car.

      • Valve announced the launch of the Steam Machine and here's what I wrote in our internal Slack: "I'm 100% serious with this: This is incredible writing. This is perfect. This is how to talk to technical customers." And after that I wrote ten more lines. I also had a fever at the time, but I think that's unrelated. It's fantastic writing.

      • "Something that keeps me up at night is the amount of miscompiled software running in the wild. There's a famous story, 'the Core 59 problem' from Facebook where seemingly random files were missing in one of their Spark databases. After herculean levels of debugging, they narrowed the problem down to a single worker box, on a single CPU core, that was literally doing math wrong. The initial bug-reproducer was 430k(!) lines of code. Eventually they managed to create a 60-line snippet of assembly that reproduced the issue 100% of the time. […]" And now it'll keep us awake at night too.

      • "Elderflower is an experimental, open source, independent single-user desktop

      OS. Linux kernel, musl libc, no existing distribution underneath. The userland is TypeScript. Apps are written in TypeScript and WASM, no native binaries. The system shell is a TypeScript REPL. All system APIs (filesystem, network, audio, app UI toolkit) are TypeScript." Watch the video here.

      • Voytek Pitula's Fintech Engineering Handbook. Lovely resource.

      • "During the first two centuries of the Roman Empire, there spread a practice known as hypomnΔ“mata, a type of notetaking system, used as a tool for meditation, in which the writer would store quotes from books they had read. Each day, often in the morning, the notetaker would open their notebook and look for a passage relevant to something they were struggling with, and then they would meditate on that--unpacking it, making the idea top of mind, ensuring it was alive in them." I'd love to do this, so I set myself the goal to collect these quotes (or videos, or tweets, or articles) and then realized: wait, that's this newsletter, isn't it?

      • This was fantastic and I wish it had been twice as long: Stop eating Lady Gaga's Oreos. It's very good. I just went through it again, trying to pick out a single quote to convince you to read it, but it's very hard. Go read it, please.

      • The Original Computer Art? Lissajous Figures Created with Oscilloscopes

      • Nabeel S. Qureshi's Principles: "A cursed fact of the world is that the most important life lessons you learn are the hardest to communicate to others. They always sound like cliches. In any case, these are a few things I've learned from experience and that I try and keep in mind." Great.

      • As someone who also loves performance optimizations, I enjoyed this very much: When Impressive Performance Gains Do Not Matter.

      • Here's a very nice pairing for you. First, watch this interview with F1 world champion Nico Rosberg. It's very honest and deep and reflective. Fascinating to hear Rosberg talk about enduring (or not) the pressure that F1 drivers are under, how he handled (or didn't) the expectations, how even when you're one of the world's best race car drivers you don't think "life is amazing!" but instead you might only feel anxiety and loneliness and not good enough. It's great and it made me wish I could talk to Rosberg and ask him some things.

      • And then watch this interview with Tom Brady. It's a fascinating conversation, both because I find what they talk about interesting (how to set a high bar in a team, "do your fucking job", "you work Monday to Saturday really, really hard, so Sunday is easy") but also because Brady is incredibly eloquent and the meta-questions of how and why he answers specific questions in certain ways are interesting. But it pairs nicely with the Rosberg interview, because Brady seems to be the complete opposite of Rosberg. Brady is incredibly confident, his mental game is his advantage; the guy sounds like he never once doubted whether he can be the greatest of all time. Rosberg on the other hand, who also became a world champion, talks about how he's very defensive, often has doubts, struggles with anxiety, and so on. And yet he also was world champion.

      • After 9 years, I changed my avatar. Strange feeling. I've followed many, many people over multiple decades on different platforms and some of them I can't picture any other way than their avatar. They are their avatar in my mind. But I felt I was kinda cheating by not weaing glasses in mine (I started wearing them in the year the original avatar photo was taken), so I recreated it and here we are. Also: wow, so many platform where one uploads their avatar.

      Like the avatar? Subscribe here:

    5. πŸ”— r/LocalLLaMA google/tabfm-1.0.0 rss

      google/tabfm-1.0.0 | TabFM is a zero-shot tabular foundation model from Google Research. It supports classification and regression on structured/tabular data with mixed numerical and categorical columns, requiring no fine-tuning or hyperparameter search - training examples are passed as context and predictions are made in a single forward pass. submitted by /u/Balance-
      [link] [comments]
      ---|---

    6. πŸ”— HexRaysSA/plugin-repository commits sync repo: +1 release rss
      sync repo: +1 release
      
      ## New releases
      - [DriverBuddyReloaded](https://github.com/voidsec/driverbuddyreloaded): 2.4.0
      
    7. πŸ”— Armin Ronacher Better Models: Worse Tools rss

      A very strange Pi issue sent me down a rabbit hole over the last two days. The short version is that newer Claude models sometimes call Pi's edit tool with extra, invented fields in the nested edits[] array. And not Haiku or some small model: Opus 4.8. The edit itself is usually correct but the arguments do not match the schema as the model invents made-up keys and Pi thus rejects the tool call and asks to try again.

      That alone is not too surprising as models emit malformed tool calls sometimes. Particularly small ones. What surprised me is that this is getting worse with newer Anthropic models as both Opus 4.8 and Sonnet 5 show it but none of the older models. In other words, the SOTA models of the family are worse at this specific tool schema than their older siblings.

      In case you are curious about Fable: I intentionally did not test it because I was not sure if the classifiers they are running might downgrade me to Opus silently.

      Tool Calls Are Text

      If you have not spent too much time looking at LLM tool calling internals, the important thing to understand is that tool calls are not magic and use some rather crude in-band signalling. The model receives a transcript, a system prompt and a list of available tools. The server munches that into a large prompt with special marker tokens. Because the model was trained and reinforced on examples of that format, at some point during generation it emits something that the API or client interprets as "call this tool with these arguments".

      For a file edit tool, the intended invocation payload might say something like this:

      {
        "path": "some/file.py",
        "edits": [
          {
            "oldText": "text to replace",
            "newText": "replacement text"
          }
        ]
      }
      

      A harness then validates the arguments, performs the edit, and feeds the result back into the model. If validation fails, the model sees an error and usually tries again.

      How exactly that formatting happens is not known for the Anthropic models, but some people have gotten out "ANTML" markers and they at times do leak also into public communications. To the best of my knowledge, the call above would come out serialized like this from the model:

      <antml:function_calls>
        <antml:invoke name="edit">
          <antml:parameter name="path">some/file.py</antml:parameter>
          <antml:parameter name="edits">
      [
        {
          "oldText": "text to replace",
          "newText": "replacement text"
        }
      ]
          </antml:parameter>
        </antml:invoke>
      </antml:function_calls>
      

      An important thing to note here is that this thing, while looking like XML, is not really XML. It's just a thing they found convenient to tokenize and train on. The other thing to note is that a basic top-level string parameter appears in-line whereas an array of objects is implemented via JSON serialization. While I'm not entirely sure that this is how it works, there are some indications that this is not too far off. This will become relevant later.

      There are two very different ways to make the model produce a structure like this:

      1. You can ask the model to produce valid JSON matching a schema and then validate it afterwards.
      2. You can constrain the sampler so that invalid JSON, or even invalid schema shapes, cannot be sampled in the first place.

      The second approach is what people usually refer to as grammar-aware or constrained decoding. The sampler masks out tokens that would violate the grammar. If the model is currently inside a JSON object and the schema says only oldText and newText are allowed, the sampler can prevent it from emitting "in_file" or "type". Grammar-aware decoding can be used both to constrain something to be syntactically valid JSON and also to enforce specific enum values or keys.

      Without any form of constraints the model is merely following a learned convention.

      The Failure

      Pi's edit tool supports multiple exact string replacements in one call. That is why the arguments contain an edits array. In the failing cases the model produces entries like this:

      {
        "oldText": "...",
        "newText": "...",
        "requireUnique": true
      }
      

      or this:

      {
        "oldText": "...",
        "newText": "...",
        "oldText2": "",
        "newText2": ""
      }
      

      Across repeated trials I saw a whole zoo of invented trailing keys: type, id, kind, unique, requireUnique, matchCase, in_file, forceMatchCount, children, notes, cost, oldText2, newText2, oldText_2, newText_2, and even an event.0.additionalProperties key inside the edit object itself.

      The most annoying part is that the actual oldText and newText payloads were byte-correct in the invalid calls I inspected. The model had in fact produced the right invocation but then added nonsense at the end of the object.

      The failure is also heavily context-dependent. A fresh single-turn prompt like "edit this file" did not reproduce it at all for me. An agentic history where the model had read files, diagnosed a problem and then composed a multi-line edit could reproduce it. And more annoyingly, not all transcripts will show that behavior. In fact, I needed Petr Baudis's transcripts to reproduce this for me at all! In that user's session continuing the session caused Opus 4.8 to fail around 20% of the time. Stripping thinking blocks from history reduced the failure rate by half. Turning on strict tool invocation eliminated it in my runs.

      Why It's Getting Worse

      My strongest hypothesis is that this is not random deterioration but a training artifact.

      When older Anthropic models were trained, they were trained on some tools (some of which were documented). But that training did not yet have a user- shipped harness like Claude Code as the obvious target. Modern Anthropic models are most likely different because their post-training includes Claude Code or a harness that looks very similar. The model learns what a successful tool call looks like in that environment. It also learns what mistakes are tolerated by that environment.

      Claude Code's own tools are comparatively flat. The ordinary edit tool is not Pi's nested edits[] shape; it is closer to file_path, old_string, new_string, and an optional flag (replace_all). Looking at Claude Code's client is very instructive: it contains retry paths for malformed tool use, parameter aliases, type coercions, Unicode repairs and filtering of unknown keys. In other words, Anthropic's own client appears to expect and accept a fair amount of slop and repairs it, mostly silently.

      If reinforcement learning happens in a harness like that, or a simulation of one, then slightly malformed tool calls can still complete the task and receive reward. The harness fully absorbs the error and there is little gradient against inventing an alias, adding a stray field or using a nearby parameter name.

      Worse, the model may become very strongly adapted to the canonical Claude Code edit tool shape. A different harness can present a tool with the same semantic intent but a different schema. Such a tool can increasingly be off- distribution. The better-trained model might actually fight you harder because its prior is stronger.

      This is not too surprising, but it is a change from how this was a few months ago. When Opus 4.5 launched, it adapted to other edit tools exceptionally well. In fact, I was pretty convinced that we're on a good path where the models are more likely to adapt to any sort of tool shape that comes around for as long as the instructions are good.

      Now I'm somewhat worried about the track we're on here. Alternative tool schemas might not just be unfamiliar. They might be implicitly punished by post-training that optimizes for one particular, forgiving tool ecology. And that ecology is not documented. While there is a text editor tool that is documented, you will see that this format is in fact not followed by Claude Code. What Claude Code does internally (which is a closed- source harness) is hidden from you.

      The Slop Harness

      Claude Code is obviously closed-source but we can look at the minified code and get some idea of what it does. And honestly, it's very forgiving of incoming data.

      For a start, Claude Code checks the model's visible text for leaked <invoke markup. It also emits some telemetry when that happens and then it has its own state machine to retry such bad calls by pushing back to the model.

      It has explicit Unicode escape repair which fixes broken \uXXXX sequences and lone surrogates in string values. It also has per-tool aliases for parameters. For instance, Edit accepts old_str (presumably from the times when the models were trained on the officially documented text editor tool), the newer old_string from the schema, new_str/new_string, path as an alias for file_path, and some more.

      It also silently filters out unexpected keys and it does not use strict mode either. The issue with strict mode is that Anthropic applies complexity limits to the tool definitions that cause API requests to fail, so presumably that's why Claude Code does not attempt to use it.

      Strictness

      Will this problem be with us in other harnesses too? One huge issue with Anthropic is that the models are completely closed, and so is the harness. Codex models are also closed, but at least the harness is not. We also have gpt-oss which is at least a bit interesting. The models are explicitly trained to use OpenAI's harmony response format and there is a lot of documentation that at least tells us how OpenAI people think about this.

      Harmony makes channels and tool-call content types part of the prompt format. A function call can look like this:

      <|start|>assistant<|channel|>commentary to=functions.get_weather
      <|constrain|>json<|message|>{"location":"San Francisco"}<|call|>
      

      The important bit is <|constrain|>json. The model can express in-band that this message body is JSON, and an inference stack can use that boundary to switch into JSON-constrained sampling for the body of the tool call. Presumably a bit of this also happens in Anthropic's models, at least in strict mode I would imagine.

      The marker in harmony helps the sampler to detect when it needs to sample with a specific grammar, and because it is part of the transcript, it makes that rather easy to do. For hosted GPT models, there is also an option to provide a LARK grammar for custom tools that need to adhere to something like this.

      Anthropic appears different from that, though maybe not entirely. If an array of objects is represented as JSON, as it appears to be, then the model has to write JSON inside the tool parameter. There is probably basic grammar- constrained sampling going on, and that may partly explain the extra keys. For a nested array parameter, that JSON includes escaped multi-line file content inside string literals, inside one tag. The unexpected, made-up keys appear exactly at the highest-entropy point of that task: after closing a several- hundred-token escaped newText string, where the model must decide } vs , "...".

      Opus 4.8 and Sonnet 5 seem to have much stronger priors about what an edit tool call should look like and that prior appears to be Claude Code's edit schema: a flat old/new string pair, plus the optional replace_all flag. My guess is that Opus has learned that an edit operation may have one extra optional field, but under Pi's nested oldText/newText shape it has no trained name for that field. So it samples a plausible name fresh each time, which is why the failures produce dozens of random keys rather than one stable alias.

      As strict mode in Anthropic appears to fix this, I presume that on the server side they are refusing to sample a key that is not permitted by the JSON schema structure. That would also explain why they have limits to the complexity of the tool definitions when strict mode is enabled.

      So far, the Codex models I tested did not show this type of regression. I tested all available ones except 5.6, which I do not have access to yet.

      What This Means For Harnesses

      The uncomfortable lesson is that tool schemas are not neutral, at least not on Anthropic models. We like to pretend that a schema is an abstract contract and the model is a general reasoner that will follow it, but that might no longer be the case for some of the tools.

      Tool schemas are somewhere in the distribution and some shapes are close to what the model saw during post-training and some are far away. Some are easy for the provider's hidden encoding (e.g. top-level attributes in ANTML), whereas some require the model to write large escaped JSON objects inside nested arrays after long multiline strings. The model may be smart enough to understand the schema and still be bad at sampling the exact shape under pressure.

      If this type of model behavior continues, I wonder what the implications for harnesses are. Obviously one could turn on strict sampling in Anthropic and the problem should go away. On the other hand, that the model has this behavior shows the impact that reinforcement learning has on them. Fighting that prior is probably futile if you want to get the best model performance.

      Right now the reality is that Claude Code is not open source and we cannot really know what they are doing in their RL environments either. We cannot assume Claude-Code-trained behavior will transfer cleanly to your tools unless they are a close match. The more post-training happens inside one dominant harness, the more every other harness will have to inherit its quirks.

      I used to be more skeptical of strict grammar-constrained tool invocation because constrained decoding can have quality tradeoffs. I still think that can be true in general, but this bug moved my priors significantly. If the newest models get better at solving the task while getting worse at faithfully emitting an alternative tool schema, then the harness needs stronger guarantees somewhere.

      If you want to find out more, or you want to discuss this, consider reading the issue on the Pi tracker.