- β
- β
- July 12, 2026
-
π r/reverseengineering APKXHunter v1.0.0 β an open-source Android Static Analysis Framework rss
submitted by /u/SyscallX-18113
[link] [comments] -
π smol-machines/smolvm pacman repo (aarch64) release
Rolling pacman repository endpoint for aarch64. Do not delete. See docs/install/arch.md.
-
π smol-machines/smolvm pacman repo (x86_64) release
Rolling pacman repository endpoint for x86_64. Do not delete. See docs/install/arch.md.
-
π smol-machines/smolvm smolvm v1.5.1 release
What's Changed
- Notify the Homebrew tap to bump its formula on each release by @BinSquare in #588
- cuda: pipelined async forwarding, CUDA graph capture/replay, and cross-connection ordering β vLLM runs end-to-end with CUDA graphs by @BinSquare in #589
- Official Arch Linux pacman repository by @BinSquare in #591
- fix(boot): don't stall 5s when agent-rootfs is read-only by @BinSquare in #592
- Display init commands progress by @mart-e in #573
- cuda: cuBLASLt descriptor fast path β eager decode 2x on loopback, in-VM eager beats native by @BinSquare in #593
- Preserve sparseness when copying disk templates during extraction by @BinSquare in #599
- release: smolvm 1.5.1 by @BinSquare in #594
New Contributors
Full Changelog :
v1.5.0...v1.5.1
-
- July 11, 2026
-
π BarutSRB/OmniWM OmniWM v0.5.5 release
What's New Since 0.5.4
- Added trackpad workspace swipes, with configurable finger count and axis handling.
- Rebuilt Hidden Bar support around safer activation, IPC v7, and a fallback icon that appears when macOS conceals OmniWM's own status item.
- Improved focus-follows-mouse so floating windows no longer lose focus to tiled windows underneath them.
- Tightened directional focus and cursor routing across vertically stacked and offset monitors.
- Kept cursor placement stable on explicit screen paths and suppressed cursor warping for mouse-click focus changes.
- Scoped layout ownership and focus per workspace, with additional recovery coverage for Niri transfers and workspace deletion.
- Enforced app minimum sizes under the no-bleed layout invariant.
- Hardened Quake surface callbacks, clipboard prompts, event intake, and service restart handling.
- Removed dead runtime state and refreshed the architecture documentation.
Release Integrity
OmniWM-v0.5.5.zipcontains the Developer ID signed, notarized, and stapled OmniWM app.OmniWM-v0.5.5.zipSHA-256:f4912f90057b2913e07a3c7cf3271dcc313e99a2cd161e4f0f3d06a782da6632GhosttyKit.xcframework-v0.5.5.zipSHA-256:17b3cbbed3ecf77bcf4480925e58a5e4d740c4786d544d683a50c34cf9aec7f2
-
π Register Spill Joy & Curiosity #91 rss
Last week, my 9-year-old started using iMovie on the iPad. She doesn't know anything about editing. She doesn't even know what editing is.
But we opened iMovie after I told her, "You can use this to cut out the part of the video where you see my hand." She said, "What do you mean, 'cut out'?"
So we cut the video up, removed some parts, moved others around. She then figured out how to add sounds and a soundtrack. Then, together (me, her, ChatGPT), we learned how to make the soundtrack play only at certain points in the video and so on.
While we were doing that, I kept thinking that a model or an agent could probably do that. Or maybe in a year. And then you could just say what you want and it would edit the video. Cut here, cut there, make this the first scene, move these around, and so on.
Then it hit me: she wouldn't know what to say, would she?
She doesn't know anything about editing. She's seen movies before, sure. That means she's seen J cuts, L cuts, jump cuts, other types of transitions, title screens, and end credits; but she doesn't know what those things are, does she? So how could she ask for them? By pointing at something else? "Make this look like that"? Would that work? Would that lead to the same results?
And that, of course, made me think about software engineering. I hope it does the same for you.
-
You can now remotely start Amp agents anywhere you can run
amp: Agents, Anywhere. We spent a lot of time this week talking to customers and after demoing the things in the Agents Anywhere post on Monday and Tuesday and one person saying "this is the best thing I've seen today, I need this" we decided to get it out as fast as possible. And here we are: agents, anywhere you want. -
We also launched The Dial which resonates a lot with people. I'm still surprised by what a difference it makes.
-
Mira Murati's Thinking Machines: The Future Worth Building Is Human. There is a lot to love here but I really, really, really, really liked these two paragraphs: "In 2014, Toyota, long a master of the automated plant, brought its expert craftsmen back onto the line with the explicit goal of growing craftsmanship and knowledge. The man who led this, Mitsuru Kawai, put the reason this way:'"To be the master of the machine, you have to have the knowledge and the skills to teach the machine.' The production of knowledge and application of intelligence lift each other; they are not substitutes. The work people do may change, and turn toward more of what only people bring, but the best organizations will make the fullest use of both. AI should enable each organization to be excellent in its own way, not to erase the differences between them." Read the whole thing and then compare it to basically everything Dario Amodei or anyone else from Anthropic has said publicly. When I make that comparison, I'd say that Thinking Machines seems to cherish humanity and Anthropic seems to fetishize Claude and would probably prefer a more human-like Claude over many humans. When I read this line in the article: "Human values, just like human knowledge, reside in the heads of individual people and resist consolidation. But today, the values and voice of AI are decided in a handful of places. A single locus of value alignment, however well run, becomes a locus of power to be captured." I can't help but think of Amodei twirling his hair in his fingers, nodding, saying that AI is going to wipe out however many double digit percent of all entry-level jobs.
-
There it is, finally: Rewriting Bun in Rust. Jarred's post on how he used Fable and "5.9 billion uncached input tokens, 690 million output tokens, and 72 billion cached input token reads -- around $165,000 at API pricing" to rewrite Bun in Rust. It's a very good, very interesting post. There's a lot to think about here, for example: "I think this would've taken 3 engineers with full context on the codebase about a year, during which time we wouldn't be able to improve Node.js compatibility, fix bugs, fix security issues or implement new features. We never would've done that. The realistic alternative was to do nothing and keep fixing the bugs at the top of this post forever." I agree with that. I think it's not something anyone would've done. But the question is: would a company that has to pay API pricing do it now? Read the whole post, this is just one part of it I found interesting, there's more in there.
-
I was this close to not linking to it, because ths newsletter is called Joy & Curiosity after all and not Jesus Christ, Man, Maybe You Shouldn 't Have Posted That? _but you _could argue that it is curious and if one of you hasn't been around for some Ruby or JavaScript drama 2010-2015 and is curious about it then this will give you a taste: Andrew Kelley's Thoughts on the Bun Rust Rewrite.
-
Half-Baked Product. If you've ever been at a startup for more than six months, you will nod to at least some of it. You could argue that the post is very cynical and I'm relatively sure it was even written to be cynical, but I don't know. I find it fascinating. Nearly 15 years of startups and when I read the post I don't how stupid the characters in the story are, but the things they experience are close to unavoidable, question is how to make the best of them.
-
Equally fascinating: How Successful Companies Go Blind. Man, I love reading stuff like this. How companies and organizations grow and change or not change, how incentives change -- I could read about that all day long.
-
If, so far, you made it through life without being neurotic about CO2 levels and air quality, but want to be, go and read this: The bottleneck might be the air in the room. I have only one critique and it's about this part: "And it is invisible from inside. Nobody in the room feels impaired. They feel a little tired, a little foggy, a little checked out, and they put it down to the length of the meeting, a bad night's sleep, or the person who won't stop talking. The one variable almost nobody checks is the air." Clearly the author has never been to Germany.
-
"The Art Institute of Chicago's API includes a
has_not_been_viewed_muchfield on artwork. It's a boolean that describes whether an art piece hasn't been visited on their website very much. [β¦] what are these artworks? Why aren't they being viewed? I can't answer the latter, but, if you have a moment for the former, please take some time to browse." -
Maybe you should learn something: "You can learn new things. Pixel art, touch typing, 3d modelling, music, calligraphy, wood working, knitting, a language. Whatever is practical and calls to you, you can learn. In the long term, learning new things is fun and makes life richer in ways you can't even imagine, and it's a time investment that will pay dividends for life as these skills never really go away. There are even social aspects, as you'll quite literally become a more interesting person to talk to." Wonderful.
-
Okay, so we all know John Gruber, author of Daring Fireball, and we also all know that when he writes a post titled Claude's Criminally Bad Electron Mac App Is an Inside Job where it's going: he's going to shit on Electron and say that it's a disgrace to the Mac and that a proper, native macOS application is far better, etc. etc. etc. That's exactly what I expected when reading that post, but -- and excuse the language here -- holy fucking shit , those last three paragraphs? I don't think I've ever seen someone attack a programmer over chosing Electron like this. Hot damn. I mean, Gruber is a great writer and I'm sure that he went over the top like this for comedic effect, but man am I glad to not be Felix Rieseberg.
-
gingerBill: Good Tools Are Invisible. I think I would've vehemently disagreed with him a few years ago on the points re: TUI and Vim. Now? I think I agree with most of it. Good post.
-
"Julius Caesar was the first propagandist. When he was off in Gaul conquering provinces, he would journal and send back snippets to Rome. He wrote so much that in Latin classes today, you study his works. When he came back to Rome, he walked in and they basically handed him the crown. Tell your story, nobody else will do it for you." Good list.
-
Lost and Found: "When something turns up at a stadium or an airport, staff photograph it, log it, and wait. Hundreds of places use one software tool for managing lost items, and I scraped their archives: thousands of accidental portraits of lost stuff."
-
I've been sitting on this next link for a solid year, waiting for just the right moment to share it. I think it's now, but you need to do me a favor: ignore every bias or prejudice you might have and go into this open-minded, okay? Alright. So here it is. It's a one hour video of Tom Platz giving a workshop on barbell squatting in a German gym. You don't have to watch the whole thing, no. But if you've squatted before or seen Tom Platz's legs, you probably want to. What I want to show you are two segments. The first one starts here and goes on for 2-3 minutes. It shows Platz coaching German bodybuilder Hoffmann through a single set of very light squats. It's only 60kg, but the number of reps isβ¦ well, insane. If you've never lifted before, yes, I know how ridiculous this looks and sounds. Platz screaming "dig! dig! one more! you're getting stronger!" I mean, it is ridiculous, grown-ass men lifting weights they don't need to lift and shouting at each other while doing so. But watch it! Watch how Hoffmann does "two more!" many, many times ; how he finds another rep somewhere and gets up again; the look he has on his face once he's done a rep and thinks he's done with the set and Platz says "two more!" again; how he then falls over and can't walk; and then how Platz says: "Congratulations, you have achieved failure." And then Hoffmann says "I think I lost the ability to go to 100%. [β¦] I have all those doubts in my head." The other thing I want you to see is the ending. After squats, they also do leg extensions and then Hoffmann talks to the camera man and again talks about going to 100%: "If you're working out alone you have to constantly remind yourself that you might be training hard, but 95% or 98% isn't enough. Today I was close to 100%. But the real art is to not only do that when there's a camera, but to always do it, even when you're alone, even when it's a Saturday evening and you're alone in the gym and no one's watching. Then you still have to push through. And you have to do it over and over again. Whenever someone asks what the difference is between a normal bodybuilder and a champion: this is it."
Are you also wondering how much about software you have to know to direct someone else to build something correctly? You should subscribe:
-
-
π Jeremy Fielding (YouTube) Engineer Vs Bee - Round 2: Building The Bee Chaser Base. rss
This work was supported by the Alfred P. Sloan Foundation, enhancing public understanding of science and technology in the modern era, in partnership with IMI: watch what matters. https://www.theimi.co/ & https://sloan.org/programs/public-understanding Check out Mitsuboshi belts πhttp://www.mblusa.com/jeremy-fielding Order custom parts Send Cut Send π http://sendcutsend.com/jeremyfielding If you want to join my community of makers and Tinkers consider getting a YouTube membership π https://www.youtube.com/@JeremyFieldingSr/join
If you want to chip in a few bucks to support these projects and teaching videos, please visit my Patreon page or Buy Me a Coffee. π https://www.patreon.com/jeremyfieldingsr π https://www.buymeacoffee.com/jeremyfielding
Social media, websites, and other channel
Instagram https://www.instagram.com/jeremy_fielding/?hl=en Twitter πhttps://twitter.com/jeremy_fielding TikTok πhttps://www.tiktok.com/@jeremy_fielding0 LinkedIn πhttps://www.linkedin.com/in/jeremy-fielding-749b55250/ My websites π https://www.jeremyfielding.com πhttps://www.fatherhoodengineered.com My other channel Fatherhood engineered channel π https://www.youtube.com/channel/UC_jX1r7deAcCJ_fTtM9x8ZA
Notes:
Check out the Formlabs 4L Printer πhttps://bit.ly/4vKl401
Technical corrections
Nothing yet
-
π r/reverseengineering Vidar Stealer 2.0: 1999 Timestamps, Explorer Hollowing & Copilot Injection rss
submitted by /u/StructBreaker
[link] [comments] -
π Anton Zhiyanov On interactive Go tours rss
Over the past two years, I've published interactive tours for five Go releases, from 1.22 to 1.26.
I know some of you have read them, and I've received a lot of kind words from you (even some core Go team members reached out) β thank you so much for that!
Tour history: Go 1.22 β’ 1.23 β’ 1.24 β’ 1.25 β’ 1.26 + Go features by version
Unfortunately, at some point, writing these tours stopped being fun and started to feel like a part-time job. I'm not really excited about that, so I've decided to stop.
I still like Go (well, most of it). I read a lot of Go code, I write some Go code, and I write Solod code, which is also Go π (Solod is a systems language with Go syntax and a Go-like stdlib).
I'm still pretty close to the language and will probably continue to write about it.
But the interactive tours story is over.
-
π smol-machines/smolvm smolvm v1.5.0 release
What's Changed
- Return 404 for a missing registry image and carry raw exec output bytes alongside the lossy text by @BinSquare in #565
- Brokered P2P layer-blob sharing (engine side) by @BinSquare in #566
- decompress zstd image layers in the guest agent, not just gzip by @BinSquare in #568
- nix/smolvm: Update to latest github release by @mmlb in #571
- Fix the clippy byte_char_slices lint failing CI on main by @BinSquare in #575
- Bump the libkrunfw submodule to 6.12.95 so overlay re-cuts carry the current guest kernel by @BinSquare in #567
- fix incomplete ssh-agent examples in the docs by @BinSquare in #576
- fix ssh-agent forwarding for non-interactive exec/run via the keep-alive container by @BinSquare in #578
- virtiofs: give user volumes and packed layers a DAX window by @BinSquare in #581
- fix: reliably kill _boot-vm child on start failure to prevent orphans by @geekgonecrazy in #583
- libkrun: bump to merged main (b982e75) and refresh all bundled libs by @BinSquare in #584
- CUDA support: forward-to-host-libs remoting with guest-RAM zero-copy by @BinSquare in #577
- cuda: auto-stage guest shims into --cuda containers and auto-discover host CUDA libraries by @BinSquare in #585
- Add Rosetta 2 x86_64 binary translation on Apple Silicon by @BinSquare in #580
- Rebuild the Windows krun.dll with disk exports and add a bundled-libkrun symbol check to CI by @BinSquare in #586
- release: smolvm 1.5.0 β guest kernel 6.12.95, zstd image layers, ssh-agent exec/run fix by @BinSquare in #579
New Contributors
Full Changelog :
v1.4.7...v1.5.0
-
- July 10, 2026
-
π IDA Plugin Updates IDA Plugin Updates on 2026-07-10 rss
IDA Plugin Updates on 2026-07-10
New Releases:
- ida_rpc v0.1.5
- mcrit-plugin mcrit-ida v1.1.7
- plugin-ida v3.20.0
- plugin-ida v3.19.0
- plugin-ida v3.18.1
Activity:
- d810-ng
- bf3cf089: refactor(plugin): copy ensure_hexrays into manager, drop d810ng import
- GhidraDec
- 0cfed4ee: Prepare Ghidra 12.1 and IDA 9.3 release
- ida-fusion-mcp
- ida_rpc
- c3e06c45: Fix minor bugs
- idavim
- da3a9a7d: Realign section banners with their contents
- c1569718: Drop the unreachable last_find guard in _repeat_find
- 35b2f822: Document that ;/, stay with IDA until f/F has been used
- 0e45eb6b: Let cw rename in the disassembly view too
- 243284c8: Shorten the manifest description and scope cw to the pseudocode view
- 972c5591: Trim dead code, skip wasted work, unify the w/e/b loop
- efe300dc: Fail closed on eventFilter exceptions while a prefix is pending
- 0504b9f3: Abandon half-typed state on any mouse press
- mcrit-plugin
- fcc3f18a: updated README
- eae80872: Merge pull request #7 from r0ny123/claude/add-matching-params-3uU7s
- d3862421: Merge branch 'main' into claude/add-matching-params-3uU7s
- 87241d9a: docs: add docstrings to test helpers, fixtures, and fake classes
- a346cd98: docs: add docstrings to all SettingsWrapper properties in config.py
- c59abec5: docs: add comprehensive docstrings to conftest.py and config.py
- d276d335: Apply suggestions from code review
- c9ca0f05: Merge pull request #6 from r0ny123/codex/document-hcli-plugin-publishing
- plugin-ida
- 9d6d67fd: Merge pull request #160 from RevEngAI/feat-PLU-316
- 09bbff95: fix: click to edit - prevents code edits
- 85e76cfa: feat(PLU-316): ai-decomp updates + logging cleanup
- f4c08192: Merge pull request #159 from RevEngAI/feat-PLU-301
- ac70f2a2: feat(PLU-301): auto unstrip re-sync
- 61037188: Merge pull request #158 from RevEngAI/feat-PLU-315
- 0514fa84: fix: tests
- rikugan
- 606cd0ce: chore(release): bump version to 1.10.2
- 178c2b0b: Merge branch 'fix/exec-python-always-visible'
- 9b62726a: test(ui): flip execute_python widget tests to always-visible behaviour
- 38771c8d: refactor(ui): rewrite ExecutePythonWidget to always-visible scrollablβ¦
- c59aef84: feat(ui): add get_tool_result_editor_style for execute_python output β¦
- d702cc0f: docs(plan): execute_python always-visible implementation plan
- 1d5760f9: docs(spec): execute_python widget always-visible design
- a0fcd1f5: chore(deps): add pyyaml to dev dependency group
- b848baa1: fix(test): add pyyaml to requirements for ida_docs_review_prompt tests
- faade18a: fix(lint): resolve ruff RUF012 and RUF059 in minimax_provider and tesβ¦
- 42873781: test: restore sys.modules state in theme_manager_signal + fix panel_cβ¦
- 2c4b7b9e: test: update minimax default model to MiniMax-M3
- 7bc300b6: chore: bump uv.lock rikugan version to 1.10.1
- 5746a799: test: fix stub isolation for settings_dialog and a2a_widget
- cf862072: fix(core): resolve mypy errors in early_log and anthropic_provider
- f69c0a6b: style(ui): apply ruff format to pre-existing files
-
π r/reverseengineering Built an eBPF debugger that answers βwho changed what and whenβ on Linux rss
submitted by /u/BeautifulFeature3650
[link] [comments] -
π HexRaysSA/plugin-repository commits sync repo: +1 plugin, +2 releases rss
sync repo: +1 plugin, +2 releases ## New plugins - [mcrit-ida](https://github.com/danielplohmann/mcrit-plugin) (1.1.7) ## New releases - [ida-rpc](https://github.com/bkerler/ida_rpc): 0.1.5 -
π r/reverseengineering I built Magic Extractor β a universal file extraction tool for Windows rss
submitted by /u/xchwarze
[link] [comments] -
π backnotprop/plannotator v0.23.0 release
Follow @plannotator on X for updates
Missed recent releases? Release | Highlights
---|---
v0.22.0 | Git-status "All changes" default review view, Commits panel with per-commit diffs, Guided Review, Pi + GitHub Copilot CLI review engines
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/diffsafter 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
What's New in v0.23.0
This is a community release: 22 PRs, seven of them authored by community contributors, five of whom are contributing for the first time. Plan approval works again on current Claude Code versions, annotate mode gains the version diff that plan mode has always had, the installer learns a binary-only mode, and a wave of Windows, OpenCode, and WebKit fixes lands. The whole changeset went through a multi-day adversarial QA pass β every commit audited, installers exercised end-to-end β before tagging.
Plan approval works again on Claude Code 2.1.199+
Claude Code 2.1.199 added a guard that discards a
PermissionRequestallow decision forExitPlanModewhenupdatedInputis absent. The result: clicking Approve in the plan review UI closed the page, but the built-in approval dialog reappeared in the terminal as if nothing happened. Deny was unaffected, which made the failure look like a Plannotator bug rather than a protocol change.The hook now echoes the plan back as
updatedInputalongside the allow decision, which satisfies the new guard on 2.1.199+ and is ignored harmlessly by older versions. If plan approval stopped working for you recently, this release fixes it.PR #1008 by @flex-yj- kim, closing #995 reported by @axelboman277.
Annotate mode: version diff for your files
Plan mode has always shown a
+N/-Mbadge when a plan is resubmitted, with a highlighted diff of what changed between versions. Annotate mode had the same diff UI sitting unused, because the annotate server never tracked history. Now it does: opening a.mdor.htmlfile saves a version keyed by file path, and re-opening the same file later shows exactly what changed since you last annotated it β same badge, same rendered diff, same Version Browser in the sidebar.HTML files annotated with
--render-htmlget the diff as the real rendered page with inline insertion/deletion highlights, not a wall of markup. History is stored under~/.plannotator/history/; if you'd rather keep annotate sessions stateless, disable it withPLANNOTATOR_ANNOTATE_HISTORY=0or{ "annotateHistory": false }in~/.plannotator/config.json. A follow-up hardening in this release also makes the history write best-effort: an unwritable data directory degrades to a normal no-diff session instead of failing to open.PR #961 by @egouilliard-leyton, who also proposed the feature in #960.
Binary-only install with
--minimalThe installer writes more than the binary: the sem semantic-diff sidecar, the agent-terminal runtime, and per-agent skills, hooks, and commands for Claude, Codex, OpenCode, Gemini, and Kiro. For users who want none of that, there was no way to opt out. Now there is:
curl -fsSL https://plannotator.ai/install.sh | bash -s -- --minimal--minimal(alias--binary-only, env varPLANNOTATOR_MINIMAL=1) installs theplannotatorbinary and stops β no sidecars, no skills, no hooks, no config writes. It works identically acrossinstall.sh,install.ps1, andinstall.cmd, and--no-minimaloverrides a persistently exported env var. The mode is non-destructive: running it over an existing full install upgrades the binary and leaves everything else alone.PR #989 by @Staninna, closing #977 reported by @wauxhall.
Reviews post as you, without attribution
Submitting a PR review to GitHub or GitLab used to append "Review from Plannotator" to the review body. That text is gone. Your general comment and file-scoped feedback post exactly as you wrote them; when GitHub requires a top-level body for an inline-only comment review, a neutral "See inline comments." is used instead; approvals and GitLab inline-only discussions stay bodyless.
PR #1033 by @backnotprop, superseding #1026 by @leoreisdias, who pushed for the change.
Windows: hooks survive spaces in your install path, installers render
cleanly
Two classes of Windows breakage are fixed. First, the generated Claude Code hook commands embedded the absolute exe path unquoted β on any machine whose profile path contains a space (
C:\Users\John Smith\...), the hook command word-split and plan review silently never intercepted. Both Windows installers now quote the path, and the install test harness pins it.Second, PR #1021 by @ShiroKSH fixed a broad set of Windows edge cases: the PowerShell installer now parses correctly under stock Windows PowerShell 5.1 (all non-ASCII characters removed, with a regression test), Amp workspace and binary paths normalize across platforms, Ask AI server turns abort cleanly when sessions reset, and Pi archive/config behaviors match Bun's. The same ASCII treatment was then applied to
install.cmd, whose status messages rendered as mojibake on default Windows code pages.Annotate folders: filter the file tree
Annotating a folder now gives you a filter row above the file tree. Type any set of words and they AND-match against file names and paths; matching folders expand automatically while you type, and Escape clears the filter before it closes the browser. Behind it, the folder scan is capped at 5,000 files (configurable via
PLANNOTATOR_FILE_BROWSER_MAX_FILES) so a giant monorepo can't hang the browser β and your own modified and untracked files are seeded into the tree first, so the files you just edited always appear no matter how large the folder is.PRs #1027 and #1022 by @backnotprop.
OpenCode: session URLs you can actually see
Remote OpenCode users (SSH, devcontainers) periodically hit the same wall: the plan or review server starts, but the URL to open it never appears anywhere visible. The root cause turned out to be structural β every URL was routed through
client.app.log, which OpenCode writes to its server log file, never the TUI. Session URLs now also surface as TUI toast notifications, delivered through the SDK's visible channel, deduplicated per session, and harmless on older OpenCode hosts that predate the toast endpoint.Two more OpenCode fixes ride along: model dropdown labels are disambiguated by provider (#1024 by @yusufemreboyraz, closing #988 reported by @ak64th), so
deepseek-v4-profrom DeepSeek and OpenRouter are tellable apart β and annotate version history is now scoped per project on OpenCode, matching the other runtimes.Additional Changes
- Comment editor focuses on open in WebKit hosts β selecting text opens the comment editor with the textarea actually focused; WKWebView hosts (like the Glimpse native window) previously required tabbing into it. PR #1031 by @BrandonNoad.
- Annotate sidebar keyboard shortcuts β toggle the Contents and Files panels from the keyboard, with the bindings listed in Settings while in annotate mode. PR #986 by @leoreisdias.
- GPT-5.6 model family in the Codex job selector β
gpt-5.6(sol),gpt-5.6-terra, andgpt-5.6-lunaare selectable for review jobs, Code Tours, and Guided Reviews. Ask AI discovers Codex models dynamically and needed no change. - First-time PR reviewers get a destination pointer β a one-time spotlight on the Agent / GitHub switcher explains that feedback can post to the PR or go to your agent session, and that double-tapping Alt toggles it.
- PR comments render tables and inline video β GitHub PR descriptions, comments, and commit messages with markdown tables or video attachments now render properly in the review Overview. PR #1007.
- HTML files render untouched β annotating an
.htmlfile renders the document byte-for-byte in a sandboxed frame instead of normalizing it. PR #1023. - Resize handles: click to collapse β panel resize handles collapse on click along their full height, with a cursor hint, and the hover highlight can be suppressed by hosts. PR #1028.
- Read-only data directories can't block sessions β an unwritable
~/.plannotator(read-only mount, full disk) now degrades annotate history and session discovery gracefully instead of failing startup. - Component library migrated to Base UI β the plan and review UIs moved from Radix to Base UI with behavior preserved, part of publishing the document UI as reusable packages. PRs #957, #1013, #1014, #1017.
- plannotator.ai/code-review β a dedicated landing page for the code review side of Plannotator, with an inline Guided Review demo. PRs #1003, #1006, #1012.
Install / Update
macOS / Linux:
curl -fsSL https://plannotator.ai/install.sh | bashWindows:
irm https://plannotator.ai/install.ps1 | iexExtra skills (compound, setup-goal, visual-explainer), opt-in:
npx skills add backnotprop/plannotator/apps/skills/extraClaude Code Plugin: Run
/pluginin Claude Code, find plannotator , and click "Update now".OpenCode: Clear cache and restart:
rm -rf ~/.bun/install/cache/@plannotatorThen in
opencode.json:{ "plugin": ["@plannotator/opencode@latest"] }Pi: Install or update the extension:
pi install npm:@plannotator/pi-extensionDroid: Install via the plugin marketplace:
droid plugin marketplace add backnotprop/plannotator droid plugin install plannotator@plannotatorAmp: 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.tsKiro CLI: The installer auto-detects Kiro and installs skills automatically. After installing the CLI, launch with:
kiro-cli chat --agent plannotatorUpgrading from before v0.20.0? Read the v0.20.0 release notes first; that release changed how skills install.
What's Changed
- fix(hook): echo tool_input as updatedInput so plan approval survives Claude Code 2.1.199+ by @flex-yj-kim in #1008
- feat(annotate): per-file version diff for .md and .html by @egouilliard-leyton in #961
- feat(install): add --minimal binary-only install mode by @Staninna in #989
- Fix Windows install and review edge cases by @ShiroKSH in #1021
- Disambiguate OpenCode model dropdown labels by provider by @yusufemreboyraz in #1024
- fix(ui): comment editor textarea not focused on open in WebKit hosts by @BrandonNoad in #1031
- feat(annotate): add sidebar shortcuts by @leoreisdias in #986
- fix(review): remove automatic platform attribution by @backnotprop in #1033
- Make the document UI reusable as published building blocks by @backnotprop in #957
- Migrate @plannotator/ui to Base UI by @backnotprop in #1013
- Migrate packages/review-editor to Base UI by @backnotprop in #1014
- Consumer enablement for @plannotator/ui by @backnotprop in #1017
- fix(review): render tables + inline video in PR comments, descriptions, and commit messages by @backnotprop in #1007
- Render arbitrary HTML in HtmlViewer without altering it by @backnotprop in #1023
- Add file browser filtering by @backnotprop in #1027
- Fix annotate terminal startup in large folders by @backnotprop in #1022
- feat(ui): handle-wide click-to-collapse + suppressible hover on resize handles by @backnotprop in #1028
- chore(ui): move to the plannotator/atomic-editor fork by @backnotprop in #1002
- Code review landing page with demo video by @backnotprop in #1003
- Code review page: copy pass + inline guided-review demo by @backnotprop in #1006
- Dedicated OG/social image for /code-review by @backnotprop in #1012
- fix(opencode): surface session URLs via TUI toasts by @backnotprop
- fix(install): quote exe path in Windows hook commands; ASCII-purge install.cmd by @backnotprop
- fix(server): unwritable data dir degrades annotate/sessions instead of failing startup by @backnotprop
- feat(ui): add GPT-5.6 family to the Codex job model catalog by @backnotprop
- feat(review): one-time spotlight on the PR feedback-destination switcher by @backnotprop
New Contributors
- @Staninna made their first contribution in #989
- @egouilliard-leyton made their first contribution in #961
- @yusufemreboyraz made their first contribution in #1024
- @BrandonNoad made their first contribution in #1031
- @ShiroKSH made their first contribution in #1021
Contributors
@flex-yj-kim tracked the Claude Code 2.1.199 protocol change to its exact guard and shipped the
updatedInputecho that makes plan approval work again (#1008), a fix most of the plugin's users will feel immediately.@egouilliard-leyton proposed the annotate version diff in #960 and then built it (#961), including the rendered-HTML diff with inline highlights. First contribution.
@Staninna built the
--minimalinstall mode across all three installer scripts (#989). First contribution.@ShiroKSH swept a wide set of Windows edge cases in one PR (#1021) β installer encoding, path normalization, Ask AI abort behavior, and Pi parity details. First contribution.
@yusufemreboyraz fixed the ambiguous OpenCode model labels (#1024). First contribution.
@BrandonNoad root-caused the WebKit focus quirk and moved the comment editor's focus to a ref callback (#1031). First contribution.
@leoreisdias added the annotate sidebar shortcuts (#986) and drove the removal of review attribution β his #1026 shaped the approach that shipped in #1033.
Community members who reported issues that drove changes in this release:
- @axelboman277: #995 (plan approval silently ignored on Claude Code β₯ 2.1.200), with @blimmer adding reproduction detail in the discussion
- @wauxhall: #977 (installer writes state the user didn't ask for)
- @ak64th: #988 (duplicate OpenCode model labels across providers)
Full Changelog :
v0.22.0...v0.23.0 -
π Anton Zhiyanov Go-flavored concurrency in C rss
Go's concurrency is one of the main reasons people like the language. You write
go f(), send values through channels, and the runtime scheduler runs thousands of goroutines on just a few OS threads. It feels effortless.None of that machinery exists in C. Which made me wonder: how close can you get to Go's concurrency model using only POSIX threads? Obviously, native OS threads can't match the efficiency of lightweight goroutines, but what is the actual cost, when does it become a problem, and is there any way to at least partially avoid it?
I ran into these questions while adding concurrency to Solod (So), a strict subset of Go that translates to plain C, with no runtime and no garbage collector. In the end, I came to the conclusion that you can do quite a lot with pthreads β as long as you're honest about the tradeoffs.
This post is about the POSIX threads-based concurrency model I chose, the benefits it offers, and its limitations.
Mutex/Cond β’ Atomics β’ Pool β’ Channel β’ Performance β’ Design β’ Wrapping up
Mutex/Cond Everything in So's concurrency stack is built on two basic POSIX primitives: the mutex and the condition variable. sync.Mutex is a thin wrapper around pthread_mutex_t: // Extracted from So's stdlib source code. type Mutex struct { mu pthread_mutex_t } func (m *Mutex) Lock() { rc := pthread_mutex_lock(&m.mu) if rc != 0 { panic("sync: Mutex.Lock failed") } } Since So translates to C, this is basically a struct that holds a pthread_mutex_t and a function that calls pthread_mutex_lock. Here's the transpiler output: // The translated C code. typedef struct sync_Mutex { pthread_mutex_t mu; } sync_Mutex; void sync_Mutex_Lock(sync_Mutex* m) { int rc = pthread_mutex_lock(&m->mu); if (rc != 0) { so_panic("sync: Mutex.Lock failed"); } } That is the whole translation β the generated C is a near-mechanical mirror of the So code, only noisier. From here on, I'll mainly show the So version, but I'll also provide the C code for those who are interested. There's nothing exciting here: sync.Mutex is a pthread mutex wrapper that panics if something goes wrong (which is rare). The companion primitive is sync.Cond, a wrapper around pthread_cond_t. It's the standard "wait until a condition holds" tool, associated with a mutex: type Cond struct // wraps pthread_cond_t + pthread_mutex_t func (c *Cond) Wait() // wraps pthread_cond_wait func (c *Cond) Signal() // wraps pthread_cond_signal func (c *Cond) Broadcast() // wraps pthread_cond_broadcast Show the translated C code typedef struct sync_Cond { pthread_cond_t cond; sync_Mutex* mu; } sync_Cond; void sync_Cond_Wait(sync_Cond* c); // wraps pthread_cond_wait void sync_Cond_Signal(sync_Cond* c); // wraps pthread_cond_signal void sync_Cond_Broadcast(sync_Cond* c); // wraps pthread_cond_broadcast These two types β Mutex and Cond β are the foundation. Other concurrency tools β Once, the thread pool, channels β are built using a mutex and one or more condition variables. This has several effects on performance, as we'll see later. Atomics Not everything needs a lock. So's sync/atomic mirrors Go's: Bool, Int32, Int64, Uint32, Uint64, and a generic Pointer[T], all with Load, Store, Swap, and CompareAndSwap methods. The nice thing is that these don't need pthreads at all. They map directly to the C compiler's __atomic builtins β the same hardware instructions that Go's compiler emits. So there's no reason for them to be any slower, and they're not: Atomic op | Go | So | Winner ---|---|---|--- Load | 2ns | 2ns | ~same Store | 2ns | 2ns | ~same CompareAndSwap | 13ns | 13ns | ~same Each number is the cost of one operation on a single thread. sync.Once is a good example of using atomics effectively. Its fast path only needs a single atomic load β after the given function runs, every future call to Do checks a flag and returns: type Once struct { mu Mutex done atomic.Bool } // Do calls f if and only if Do is being called // for the first time for this o. func (o *Once) Do(f func()) { if o.done.Load() { // lock-free fast path return } // slow path... } Show the translated C code typedef struct sync_Once { sync_Mutex mu; atomic_Bool done; } sync_Once; // Do calls f if and only if Do is being called // for the first time for this o. void sync_Once_Do(sync_Once* o, void (*f)()) { if (atomic_Bool_Load(&o->done)) { // lock-free fast path return; } // slow path... } Worker pool To actually run code concurrently, you need threads. The conc.Thread type wraps pthread_t and its related functions: type Thread struct // wraps pthread_t func (th Thread) Wait() any // wraps pthread_join func (th Thread) Detach() // wraps pthread_detach Show the translated C code typedef struct conc_Thread { pthread_t t; } conc_Thread; void* conc_Thread_Wait(conc_Thread th); // wraps pthread_join void conc_Thread_Detach(conc_Thread th); // wraps pthread_detach Consider this conc.Go function: // Go launches an OS thread that runs fn(arg) and returns a handle to it. func Go(entry func(any) any, arg any) Thread { var th Thread rc := pthread_create(&th.t, nil, entry, arg) // ... } Show the translated C code // Go launches an OS thread that runs fn(arg) and returns a handle to it. // `any` in So translates to `void*` in C. conc_Thread conc_Go(void* (*entry)(void*), void* arg) { conc_Thread th = {0}; int rc = pthread_create(&th.t, NULL, entry, arg); // ... } Usage example: func work(arg any) any { acc := arg.(*Account) // ... } func main() { var acc Account th := conc.Go(work, &acc) // ... do other work concurrently ... th.Wait() // work is complete once Wait returns } Show the translated C code void* work(void* arg) { main_Account* acc = (main_Account*)arg; // ... } int main(void) { main_Account acc = {0}; conc_Thread th = conc_Go(work, &acc); // ... do other work concurrently ... conc_Thread_Wait(th); // work is complete once Wait returns } It might look like go work(&acc), but that's just on the surface. conc.Go starts an actual OS thread, not a goroutine. You have to eventually call Wait to join or Detach it, or else its resources will leak. Also, OS threads are expensive to create β they're nothing like Go's goroutines, which only need a few kilobytes of stack and start up in nanoseconds. That's exactly why you usually don't want to call Go inside a loop. For tasks that are short-lived or happen often, it's better to use a pool of long- lived worker threads and send tasks to them. conc.Pool to the rescue: Worker thread pool in So ββββββββββ ββββββββββ ββββββββββ β Task 1 β β Task 2 β...β Task M β M tasks ββββββββββ ββββββββββ ββββββββββ ββββββββββββββββββββββββββββββββββ β conc.Pool β coordinator ββββββββββββββββββββββββββββββββββ ββββββββββ ββββββββββ ββββββββββ β Thrd 1 β β Thrd 2 β...β Thrd N β N threads, N << M ββββββββββ ββββββββββ ββββββββββ ββββββββββββββββββββββββββββββββββ β OS scheduler β ββββββββββββββββββββββββββββββββββ Usage example: type Task struct { in int out int } func square(arg any) { task := arg.(*Task) task.out = task.in * task.in } func main() { tasks := make([]Task, 10) opts := conc.PoolOpts{NumThreads: 2} pool := conc.NewPool(mem.System, opts) defer pool.Free() for i := range tasks { tasks[i].in = i pool.Go(square, &tasks[i]) } pool.Wait() } Show the translated C code typedef struct main_Task { so_int in; so_int out; } main_Task; void square(void* arg) { main_Task* task = (main_Task*)arg; task->out = task->in * task->in; } int main(void) { so_Slice tasks = so_make_slice(main_Task, 10, 10); conc_PoolOpts opts = (conc_PoolOpts){.NumThreads = 2}; conc_Pool* pool = conc_NewPool(mem_System, opts); for (so_int i = 0; i < so_len(tasks); i++) { // so_at is a generic macro to get the i-th element of a // specific type (main_Task here) from a type-erased slice. // Here we're getting the i-th task from the tasks slice. so_at(main_Task, tasks, i).in = i; conc_Pool_Go(pool, square, &so_at(main_Task, tasks, i)); } conc_Pool_Wait(pool); conc_Pool_Free(pool); } The first argument to NewPool, mem.System, is a memory allocator. Solod avoids hidden allocations, so anything that needs memory takes an allocator explicitly β here it backs the pool's task queue. Under the hood, a Pool is a fixed group of worker threads that pull tasks from a shared queue (a ring buffer). It uses one mutex and a few condition variables: // Pool is a bounded pool of worker threads with a wait queue // which execute tasks of the form func(any). type Pool struct { alloc mem.Allocator mu sync.Mutex notEmpty sync.Cond // signaled when a task is enqueued notFull sync.Cond // signaled when a slot frees allDone sync.Cond // broadcast when no task is in flight workers []Thread queue []task // ring buffer of submitted tasks active int // tasks submitted but not yet finished stopped bool // set by Free to drain and exit } // NewPool creates a pool with a given number // of worker threads and starts them. func NewPool(alloc mem.Allocator, opts PoolOpts) *Pool // Go submits a task for execution, blocking while the queue is full. func (p *Pool) Go(fn func(any), arg any) // Wait blocks until all submitted tasks finish. func (p *Pool) Wait() Show the translated C code // Pool is a bounded pool of worker threads with a wait queue // which execute tasks of the form func(any). typedef struct conc_Pool { mem_Allocator alloc; sync_Mutex mu; sync_Cond notEmpty; // signaled when a task is enqueued sync_Cond notFull; // signaled when a slot frees sync_Cond allDone; // broadcast when no task is in flight so_Slice workers; so_Slice queue; // ring buffer of submitted tasks so_int active; // tasks submitted but not yet finished bool stopped; // set by Free to drain and exit } conc_Pool; conc_Pool* conc_NewPool(mem_Allocator alloc, conc_PoolOpts opts); void conc_Pool_Go(conc_Pool* p, void (*fn)(void*), void* arg); void conc_Pool_Wait(conc_Pool* p); notEmpty wakes up a worker when there are tasks to do, notFull applies back-pressure when the queue is full, and allDone lets Wait know when everything is finished. It's a classic producer-consumer setup, about 200 lines of code, and there's nothing fancy about it. The heart of the pool is the worker loop. Each thread blocks until a task appears, runs it outside the lock so workers execute in parallel, then records that it finished: // workerMain runs on every pool thread: pull a task, run it, repeat. func workerMain(arg any) any { p := arg.(*Pool) for { p.mu.Lock() for p.qempty() && !p.stopped { p.notEmpty.Wait() // sleep until a task is enqueued } if p.qempty() && p.stopped { p.mu.Unlock() break // queue drained and pool shutting down } t := p.qpop() p.notFull.Signal() // a slot freed for a waiting submitter p.mu.Unlock() t.fn(t.arg) // run the task with the lock released p.mu.Lock() p.active-- if p.active == 0 { p.allDone.Broadcast() // wake anyone parked in Wait } p.mu.Unlock() } return nil } Show the translated C code // workerMain runs on every pool thread: pull a task, run it, repeat. static void* workerMain(void* arg) { conc_Pool* p = (conc_Pool*)arg; for (;;) { sync_Mutex_Lock(&p->mu); for (; conc_Pool_qempty(p) && !p->stopped;) { sync_Cond_Wait(&p->notEmpty); // sleep until a task is enqueued } if (conc_Pool_qempty(p) && p->stopped) { sync_Mutex_Unlock(&p->mu); break; // queue drained and pool shutting down } task t = conc_Pool_qpop(p); sync_Cond_Signal(&p->notFull); // a slot freed for a waiting submitter sync_Mutex_Unlock(&p->mu); t.fn(t.arg); // run the task with the lock released sync_Mutex_Lock(&p->mu); p->active--; if (p->active == 0) { sync_Cond_Broadcast(&p->allDone); // wake anyone parked in Wait } sync_Mutex_Unlock(&p->mu); } return NULL; } This is what separates a pool from a plain queue. Pool.Go bumps active as it enqueues; each worker decrements it after running a task, and the last one out broadcasts allDone. Pool.Wait sleeps until the count hits zero: // Wait blocks until every submitted task has finished. func (p *Pool) Wait() { p.mu.Lock() for p.active != 0 { p.allDone.Wait() } p.mu.Unlock() } Show the translated C code // Wait blocks until every submitted task has finished. void conc_Pool_Wait(conc_Pool* p) { sync_Mutex_Lock(&p->mu); for (; p->active != 0;) { sync_Cond_Wait(&p->allDone); } sync_Mutex_Unlock(&p->mu); } The tradeoff is that the number of worker threads is fixed. In Go, a program can handle thousands of concurrent I/O waits because blocked goroutines use very little memory. A So pool can't do this β if all N workers are parked on a blocking syscall, the pool is stalled until one returns. You have to set the pool size based on the workload, instead of letting the runtime manage it for you. Channel Channels are an important part of Go's concurrency model, and So's conc.Chan[T] gives you something quite similar. Just like in Go, it passes values by copy and comes in buffered and unbuffered flavors: ch := conc.NewChan // buffered, capacity 2 defer ch.Free() // Producer on its own thread. prod := producer{ch: &ch, n: 5} thr := conc.Go(produce, &prod) defer thr.Wait() // Consume until the channel is closed and drained. var v int for ch.Recv(&v) { fmt.Printf("received %d\n", v) } Show the translated C code // conc_NewChan, conc_Chan_Recv, and friends are generic macros: // the element type (so_int here) is passed as the first argument. conc_Chan ch = conc_NewChan(so_int, mem_System, 2); // buffered, capacity 2 // Producer on its own thread. producer prod = (producer){.ch = &ch, .n = 5}; conc_Thread thr = conc_Go(produce, &prod); // Consume until the channel is closed and drained. so_int v = 0; for (; conc_Chan_Recv(so_int, &ch, &v);) { fmt_Printf("received %d\n", v); } conc_Thread_Wait(thr); conc_Chan_Free(so_int, &ch); Chan[T] is a thin generic shell over one of two engines, picked at creation time: Buffered (n > 0) is a mutex-guarded ring buffer with notEmpty and notFull condition variables β like the Pool queue. Senders block when it's full, receivers block when it's empty. type Buffer struct { alloc mem.Allocator mu sync.Mutex notEmpty sync.Cond // signaled when an item becomes available notFull sync.Cond // signaled when a slot frees buf mem.Array // ring buffer closed bool // true after Close } // Send copies v into the ring, blocking while it is full. func (ch *Buffer) Send(v any) { ch.mu.Lock() for ch.bfull() { ch.notFull.Wait() // back-pressure until a slot frees } ch.bpush(v) ch.notEmpty.Signal() // wake one waiting receiver ch.mu.Unlock() } Show the translated C code typedef struct conc_Buffer { mem_Allocator alloc; sync_Mutex mu; sync_Cond notEmpty; // signaled when an item becomes available sync_Cond notFull; // signaled when a slot frees mem_Array buf; // ring buffer bool closed; // true after Close } conc_Buffer; // Send copies v into the ring, blocking while it is full. void conc_Buffer_Send(conc_Buffer* ch, void* v) { sync_Mutex_Lock(&ch->mu); for (; conc_Buffer_bfull(ch);) { sync_Cond_Wait(&ch->notFull); // back-pressure until a slot frees } conc_Buffer_bpush(ch, v); sync_Cond_Signal(&ch->notEmpty); // wake one waiting receiver sync_Mutex_Unlock(&ch->mu); } The full implementation also checks for closed, but I left it out for brevity. Recv is the mirror method: block while empty, pop the next value, signal notFull to wake a sender. It also handles the closed channel, returning false once the buffer is closed and drained. The rest is this lock-wait- signal core. Buffer source code Unbuffered (n == 0) is a rendezvous: each send blocks until a receiver takes the value, copying vsize bytes directly from the sender's stack to the receiver's destination without using an intermediate buffer. type Rendezvous struct { alloc mem.Allocator vsize int // size in bytes of a handed-off value mu sync.Mutex cond sync.Cond // broadcast on every slot state change src any // the sender's published value (valid while full) full bool // a value is published and not yet freed claimed bool // the published value has been taken by a receiver closed bool // true after Close } // Send publishes v and waits for a receiver to take it. func (ch *Rendezvous) Send(v any) { ch.mu.Lock() for ch.full { ch.cond.Wait() // wait for the previous hand-off to finish } ch.src, ch.full, ch.claimed = v, true, false // publish ch.cond.Broadcast() // wakeup #1: wake a receiver for !ch.claimed { ch.cond.Wait() // wait until the value is taken } ch.src, ch.full = nil, false // free the slot ch.cond.Broadcast() ch.mu.Unlock() } Show the translated C code typedef struct conc_Rendezvous { mem_Allocator alloc; so_int vsize; // size in bytes of a handed-off value sync_Mutex mu; sync_Cond cond; // broadcast on every slot state change void* src; // the sender's published value (valid while full) bool full; // a value is published and not yet freed bool claimed; // the published value has been taken by a receiver bool closed; // true after Close } conc_Rendezvous; // Send publishes v and waits for a receiver to take it. void conc_Rendezvous_Send(conc_Rendezvous* ch, void* v) { sync_Mutex_Lock(&ch->mu); for (; ch->full;) { sync_Cond_Wait(&ch->cond); // wait for the previous hand-off to finish } ch->src = v; // publish ch->full = true; ch->claimed = false; sync_Cond_Broadcast(&ch->cond); // wakeup #1: wake a receiver for (; !ch->claimed;) { sync_Cond_Wait(&ch->cond); // wait until the value is taken } ch->full = false; // free the slot ch->src = NULL; sync_Cond_Broadcast(&ch->cond); sync_Mutex_Unlock(&ch->mu); } Recv is the other half: it waits for a published, unclaimed value, copies vsize bytes straight from the sender's stack into dst (no intermediate buffer), marks it as claimed, and broadcasts to wake the sender back, creating wakeup #2. One hand-off, two wakeups. Copying directly from the sender's stack is safe because of that second wakeup. src is a pointer to v, which lives on the sender's stack. While the receiver is reading it, the sender is parked in for !ch.claimed { ch.cond.Wait() }, so its stack frame stays alive. The sender only returns (and reclaims that memory) after the receiver sets claimed and wakes it up. There's no need to copy into a shared buffer because the source is guaranteed to outlive the read. Rendezvous source code As you can see, the API is pretty similar to Go. Now let's look at the numbers. Performance
Here's the main tradeoff: pthread-based concurrency primitives are fast when no one has to block, but they get slow when someone does. And it's always for the same reason.
Go schedules goroutines in userspace. When one goroutine blocks on a channel and another wakes it up, the runtime moves them between its own queues β no kernel involved. POSIX threads, on the other hand, don't provide a userland scheduler. When a thread blocks on a condition variable, it parks in the kernel, and waking it up requires a syscall. Every hand-off between threads that actually parks pays the cost of a syscall on both ends.
You can clearly see the difference in the mutex benchmarks. With 8 competing threads, it all comes down to whether the waiting threads have to park or not:
Mutex benchmark | Go | So | Winner
---|---|---|---
Uncontended, 1 thread | 14ns | 9ns | So - 1.6x
Contended spin, 8 threads | 75ns | 27ns | So - 2.8x
Contended work, 8 threads | 1.1Β΅s | 2.0Β΅s | Go - 1.8xEach number is the average time for a single
Lock/Unlockpair. The uncontended benchmark runs on one thread, while the contended benchmarks have multiple threads fighting over the same mutex.Notice that So actually wins the first two benchmarks, and for good reason. So's
Lockis a plainpthread_mutex_lockcall with nothing extra, while Go'ssync.Mutexadds more overhead β like starvation-mode tracking and a runtime that stays involved because a goroutine can be preempted in the middle of a critical section.When nobody parks, that overhead is the main cost, and the thinner wrapper is closer to the hardware. With an empty critical section (the spin benchmark), a waiting thread grabs the lock while still spinning and almost never parks β So wins by 2.8x. The uncontended benchmark (a single thread, no contention) shows the same thing: less code between the call and the lock, so 9ns versus 14ns.
The picture flips the moment threads have to park. Give the critical section about a microsecond of real work (the work benchmark) and waiters exhaust their spin budget and park. Now every hand-off costs a wakeup syscall, and So drops to half of Go's throughput. The work is identical in both cases β the difference comes from the parking cost.
Condition variables demonstrate this clearly because they always park:
Cond benchmark | Go | So | Winner
---|---|---|---
1 waiter | 150ns | 1.5Β΅s | Go - 10x
8 waiters | 2.0Β΅s | 14Β΅s | Go - 7.0x
32 waiters | 9.0Β΅s | 60Β΅s | Go - 6.7xEach number is the cost of one rendezvous round: a single broadcast that wakes every waiter and hands control back, with N waiters plus one broadcaster.
Pthread-based condition variable is consistently 7-10 times slower. There's no trick to close this gap β it's just the cost of waking up a real OS thread instead of a goroutine.
Channels have the same issue because they're built using mutexes and condition variables:
Chan benchmark | Go | So | Winner
---|---|---|---
Uncontended, 1 thread | 24ns | 21ns | So - 1.1x
Unbuffered, 2 threads | 130ns | 3.0Β΅s | Go - 23x
Buffered (10), 2 threads | 44ns | 400ns | Go - 9.1x
Buffered (100), 2 threads | 33ns | 70ns | Go - 2.1xEach number is the cost of moving one value through the channel (send plus its matching receive). The number in parentheses is the buffer capacity.
The uncontended case fills and drains a buffer from a single thread, so nothing ever blocks β it's just a lock plus a copy, which gives So a slight advantage. But the moment a producer and consumer actually start handing off work, So has to wake up a thread for every transfer that gets parked. It's worst for the unbuffered channel, where every value is a rendezvous with two wakeups: 23x slower. A larger buffer helps a lot β with room for 100 items, most sends go through without waking anyone, and the gap narrows to about 2x.
The consequence is that the larger your tasks are, the better pthread-based concurrency works. If you use a channel for fine-grained, value-at-a-time streaming between threads, performance will suffer. But if you use a channel to pass whole work items to a pool, where each item takes tens of microseconds to process, the wakeup cost becomes negligible. The pool benchmarks on realistic workloads confirms this:
Pool benchmark | Go | So | Winner
---|---|---|---
1000 CPU tasks (~40Β΅s each) | 7ms | 8ms | Go - 1.1x
64 IO tasks (1ms block each) | 9ms | 10ms | Go - 1.1xEach number is the wall-clock time for 8 workers to process the whole batch.
Here, So is within 1.1x of Go. The per-task dispatch cost is still present, but it's spread out over real work, and the performance penalty is pretty small.
Benchmarking
All benchmarks were run on an Apple M1 CPU running macOS. The C code was compiled with Clang 16 using these CFLAGS and mimalloc as the system allocator:
-Ofast -march=native -flto -funroll-loops -DNDEBUGThe results shown are the medians from several benchmark runs. Each benchmark ran many iterations, following the same logic as Go's own benchmarking.
The Go benchmarks used Go 1.26 and
go test -bench=..Source code for both So's and Go's benchmarks: conc β’ sync
Here's a summary of the strengths and weaknesses of the pthread-based approach:
- β Coarse-grained pooled workloads are within about 10% of Go's performance.
- β Uncontended locks and spin-friendly critical sections perform quite well.
- β Atomic operations are as fast as in Go.
- β The implementation is 100x simpler.
- β Anything that needs to park and wake an OS thread is much slower than Go's userspace scheduler.
- β The pool can't handle thousands of blocked waiters like goroutines can.
If you're looking for "thousands of cheap goroutines", the pthread-based approach will let you down. But if you're fine with "a few worker threads handling lots of tasks", it holds up well.
Design decisions
Three decisions influenced the way I implemented concurrency in Solod.
Pthreads, not fibers. I know there are coroutine/fiber libraries for C that avoid the kernel wakeup cost β single-threaded ones like neco, and multi-threaded ones like libfiber. A userspace scheduler is exactly what would help to match Go in the benchmarks above.
I decided not to use one. I wanted something dead simple β an approach I could explain in a paragraph, using tools every C programmer already knows. The trade-off is that you lose some performance with fine-grained blocking, but in many real-world situations, pthreads work fine if you use a worker pool. For me, keeping things simple is more important than saving a few microseconds during task hand-offs. For now, at least.
Standard library, not language. Go bakes goroutines, channels, and select right into the language. I decided to keep everything in the stdlib for two reasons.
β It follows So's "no hidden allocations" rule. In Go,
go f()quietly allocates a goroutine stack, andmake(chan T, n)allocates a buffer. In So, all allocations are explicit: you pass an allocator toNewChanandNewPool, and you always know exactly where the memory comes from β whether it's the system allocator, an arena, or something else.β A library is more flexible. Since a pool is a regular value, you can have as many as you need, each sized for its specific purpose. In a multi-stage pipeline where each stage needs a different capacity, you can start one pool per stage, each with its own
NumThreadsandQueueSize, instead of being given a single global scheduler. The language stays simple, and the flexibility is in code you can easily read.Timeouts, not select. Go's
selectwaits on several channel operations at once and proceeds with whichever is ready first. Implementing it would require a lot of work β a thread has to register interest on multiple channels, block once, and then wake up when any of them is ready β so I left it out. Instead,ChanoffersSendTimeoutandRecvTimeout, which cover two common uses ofselectwith a single channel:- "Do this, but give up after a while" (Go's
case <-time.After(...)idiom). - "Do this only if it won't block" (Go's non-blocking
defaultbranch).
What's missing is the ability to block on multiple channels at once and continue with whichever one is ready first, as well as the option to mix sends and receives in the same selection.
Wrapping up
How close can you get to Go's concurrency using only pthreads? Close enough to be useful, but not enough to really match Go. You can wrap real OS threads with familiar APIs β mutexes, condition variables, pools, channels β and the code will look and act a lot like Go, at least until a thread needs to block. But there's no scheduler underneath, so when a thread blocks, it's an actual thread waiting in the kernel, not a goroutine that's paused for free. That's the main limitation of this approach.
What you get in return is brutal simplicity. Every primitive is a thin wrapper with no runtime hiding behind it, so the performance is exactly what the OS gives you: fast atomics, fast uncontended locks, and pooled throughput within ~10% of Go on coarse-grained work. But as soon as you switch to fine-grained, one-value-at-a-time hand-offs, the cost of kernel wakeups becomes the main factor, and you'll notice the slowdown.
If you think the pthread approach might work for you, I invite you to try Solod. It includes the
syncandconcpackages, along with many others ported from Go's standard library. -
π r/reverseengineering I reverse-engineered the DJI Spark smart battery I2C/SMBus protocol and documented the captures, firmware, and hardware rss
submitted by /u/embeddedbyesad
[link] [comments] -
π Barre/ZeroFS v2.0.9 release
What's Changed
- Make HA segment materialization atomic by @Barre in #526
- Replace extent publication barrier with RwLock by @Barre in #527
Full Changelog :
v2.0.8...v2.0.9 -
π r/reverseengineering Old game (Windows, Ka'Roo 2000) rewrite + reverse engineered rss
submitted by /u/Cahb_UA
[link] [comments] -
π r/reverseengineering Detecting Hardware Trojans in High-Level Synthesis-Generated RTL using Large Language Models rss
submitted by /u/asankhs
[link] [comments] -
π r/reverseengineering Reverse-engineering a Neopets shop with 48,000 inventory snapshots rss
submitted by /u/danielrmay
[link] [comments] -
π Barre/ZeroFS v2.0.8 release
What's Changed
- Add deterministic simulation testing for the extent-store data plane by @Barre in #519
- Fuzz and harden the 9P and NBD protocol decoders by @Barre in #521
- Fail closed on segment materialization HEAD errors by @Barre in #522
- Backpressure extent writes before growing an overdue open segment by @Barre in #520
Full Changelog :
v2.0.7...v2.0.8 -
π New Music Releases Bring Me the Horizon - Count Your Blessings | Repented rss
Bring Me the Horizon - a new release is available:
- 2026-07-10: Count Your Blessings | Repented (Album)
Amazon: Canada | Deutschland | France | United Kingdom | United States
Visit muspy for more information.
-
π New Music Releases If These Trees Could Talk - The Hidden Hand rss
If These Trees Could Talk - a new release is available:
- 2026-07-10: The Hidden Hand (Album)
Amazon: Canada | Deutschland | France | United Kingdom | United States
Visit muspy for more information.
-
- July 09, 2026
-
π IDA Plugin Updates IDA Plugin Updates on 2026-07-09 rss
IDA Plugin Updates on 2026-07-09
Activity:
- ghidra
- c7057c19: GP-0: Adding Markdown support to Doclets
- ida-domain
- ida-hcli
- 814026b5: AI-assisted: fix: ida open, support ida:/// URLs (dβ¦
- playlist
- project
- 75131be4: added cfunctions from ghidra
- rikugan
- 26e35fd6: chore(release): bump version to 1.10.1
- 75a9066c: fix(ui): clear stale height pin in _HeightCachedLabel
- 3961b78c: fix(ui): remove double-spacing between paragraphs
- db63d535: docs(readme): bump version badges to 1.10.0
- 3b6bd644: fix(ui): migrate ExecutePythonWidget to bind_theme
- 361c29e3: Updating README and fixing UI setting
- eacf6a3a: Merge remote-tracking branch 'EliteClassRoom/master'
- 5d8f986a: chore(release): bump version to 1.10.0
- bf1621a4: Merge branch 'feat/execute-python-unified-widget'
- 83884131: fix(ui): hide code section + result frame when collapsed to remove gap
- 9c447004: fix(ui): hide Result label when collapsed
- ghidra
-
π Simon Willison The new GPT-5.6 family: Luna, Terra, Sol rss
OpenAI's latest flagship model hit general availability this morning, and comes in three sizes: Luna, Terra, and Sol (from smallest to largest).
The new models are priced per 1M input/output tokens as Luna $1/$6, Terra $2.50/$15, Sol $5/$30. For comparison, the Claude Opus series are $5/$25 and the Claude Fable 5 is $10/$50, but price-per-million tokens doesn't tell us much now that the number of reasoning tokens can differ so much between models for the same task.
All three models have a February 16th 2026 knowledge cutoff, a million token context window, and 128,000 maximum output tokens.
OpenAI's biggest benchmark claim concerns long-running agentic performance, with one benchmark showing all three models outperforming Claude Fable 5:
We trained GPT-5.6 to get more useful work from every token. On Agentsβ Last Exam, an evaluation of long-running professional workflows across 55 fields, GPT-5.6 Sol sets a new high of 53.6, eclipsing Claude Fable 5 (adaptive reasoning) by 13.1 points. Even at medium reasoning, it beats Fable 5 by 11.4 points at roughly one-quarter the estimated cost. That efficiency extends to smaller models, which are essential to making intelligence more abundant and affordable: GPT-5.6 Terra and GPT-5.6 Luna outperform Fable 5 at around one-sixteenth the cost.
Amusingly, one self-reported benchmark that Fable 5 crushed the GPT-5.6 family on was SWE-Bench Pro, where Fable 5 got 80% compared to GPT-5.6 Sol getting 64.6%. This may help explain why OpenAI chose to publish this article yesterday specifically calling out SWE-Bench Pro for problems they found while auditing that benchmark:
In light of these results, we estimate that ~30% of SWE-bench Pro tasks are broken, and advise that model developers carefully examine results
I've had some early access to GPT-5.6 Sol - it's definitely very competent, though so far it hasn't struck me as better than Fable at the kind of complex coding tasks I've been using with Anthropic's model.
As usual, the model guidance for using GPT-5.6 has the most interesting details. There are a bunch of new API features that I need to explore (and probably add support for in LLM), including:
- Programmatic Tool Calling allows the models to "compose and run JavaScript that orchestrates tool calls" - which sounds to me like it could help bridge the gap between MCPs and full terminal sessions that can compose CLI utilities in useful ways. Also reminiscent of the dynamic filtering mechanism Anthropic added to their web search tool, which allows code execution against web results as part of a single model turn.
- Multi-agent lets the model "spin up subagents for parallel, focused work" - the sub-agent pattern now baked into the core API.
- Prompt cache breakpoints brings the Claude model of prompt caching to OpenAI, letting you be explicit about where the cache breakpoints are rather than relying on the API to detect them automatically. Personally I much prefer automatic detection (still supported by OpenAI), but presumably there are optimization cost savings to be had here if you put the work in.
- You can now set detail: original on image requests to avoid resizing the image at all before it is processed.
Here's a full page with 18 different pelicans - for reasoning efforts none, low, medium, high, xhigh, and max across the three different models. It also lists their token and calculated costs - the least expensive was gpt-5.6-luna at effort none for 0.71 cents, the most expensive was gpt-5.6-sol at max reasoning level for 48.55 cents.

In further pelican news, if you jump to 17:50 in their livestream from this morning you'll see OpenAI's own demo of 3D pelicans riding a tricycle, a bicycle, a pony, and another pelican!

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.
-
π The Pragmatic Engineer The Pulse: Interesting AI coding stats from Cursor rss
Hi, this is Gergely with a bonus, free issue of the Pragmatic Engineer Newsletter. In every issue, I cover Big Tech and startups through the lens of senior engineers and engineering leaders. Today, we cover one out of four topics _a past _The Pulse issue__ . Full subscribers received the article below five weeks ago. If you 've been forwarded this email, you can subscribe here .
Cursor has just released a new report based on two years of its aggregated usage data, and there are some interesting findings:
Power users generate 10x as many lines of code vs the median
Source:CursorThe median dev using Cursor (the p50) generates about 700 lines of code per week with it, while for the 90th percentile, it's closer to 9,000 lines.
Top 1% of users create incredible volume of code
The p99 data is pretty stunning:
The
top 1% of Cursor users (p99) vs the top 10% (p90)The top 1% of users generate around 30-40K lines of code per week! That's the equivalent of what ~45 "median" devs generate in the same period.
It's worth asking how these top 1% of users are different. Are they writing a lot more greenfield code, do they have a bias for not using libraries, are they tokenmaxxing to get to the top of leaderboards? Do they generate 45x as many bugs, and importantly: are they adding a lot of business value with the software they ship?
Cursor consumes 10x more input tokens than it generates in output tokens
This is surprising: 90% of Cursor's token usage is input tokens! This means that most of the tokens used are for reading the existing codebase and documentation. Outputting of code is a minority usage:
Input
tokens (Cursor reading the codebase) is the bulk of token usageIn some ways, this usage makes sense: as devs, we always spent far more time on reading the code, compared to lines of code we typed out. The "10:1 read- to-write" ratio is a classic. Here's Robert. C. Martin (aka "uncle Bob") sharing this observation in 2008, in his book, Clean Code:
"Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code⦠[Therefore] making it easy to read, makes it easier to write."
I find it amusing that we're now seeing this 10:1 read / write ratio for token usage with AI agents!
Input tokens become the main AI token cost
Input tokens are priced at a fraction of output tokens: for example, Opus 4.7 charges 5x more for output tokens than for input tokens ($5 per 1 million input tokens and $25 per 1 million output tokens). Still, thanks to input tokens dominating token usage, Cursor is seeing input tokens account for closer to 70% of the cost of AI coding agents:
Input
tokens dominate Cursor costsWithout caching context, token cost would be 10x higher
Cursor does smart caching of context, to avoid re-generating old context with more new input tokens. When taking cache usage into account, Cursor only spends 0.6% of tokens on output tokens. The remaining 99% is split between cache read (90%), cache write (2.5%), and input tokens (7%):
Output
tokens are only 0.6% of token usage when considering cache reads & writesI wonder if context reuse and caching will be a key AI efficiency component in the future? AI tokens are expensive to generate, so any form of reuse will make a lot of sense, especially in workflows like coding where a lot of existing context is reused.
Of course, Cursor sharing this detail also makes sense, as they remind everyone that building an efficient AI agent harness is far from trivial. Indeed, if you roll your own agent harness, you also need to put an efficient caching layer in place to match the efficiency of tools like Cursor.
Opus is the most expensive model & could hurt Anthropic
At the time of publishing, Opus 4.7 was still considered the most capable coding model. However, it's also very expensive, and Cursor's own data shows it's close to 10x more expensive than its own Composer 2.5 model:
Opus
4.7 is twice as expensive as GPT-5.5 & nearly 10x more than Composer 2.5It's significant that Cursor compares the cost of a single agent request; it's not a direct token-to-token comparison. And it's worth noting this benchmark is being shared by Cursor, which has an incentive for its Composer model to appear the lowest-cost.
Still, assuming you can get similar-enough results with a 10x cheaper model, it is a saving that's hard to ignore, especially for mid-sized and above companies. I would not be surprised if more tech companies find ways for devs to use less capable - but cheaper - models for less critical work.
More expensive models result in higher acceptance rates
An interesting metric Cursor shares is cost-per-line-added, per model:

This metric is a more realistic cost because it correlates to output: "smart" models that are expensive, but which produce code that is frequently accepted, are penalized by the cost-per-agent-request metric, but they're not here.
Indeed, Opus 4.7 has the same cost-per-line-accepted as GPT 5.5 at half the cost per agent request. In this comparison, Cursor's Composer model is "only" 5x as efficient.
Missing from both lists are Google's Gemini models, a strange omission by Cursor. I reached out to Cursor and they told me that Gemini was left out simply because they see very little usage of this model on their platform, similar to the sparsely used Grok model.
Almost half of AI changes accepted without manual review by devs
I've left the most interesting part of this report to last: in just a month, among devs using Cursor, it has gone from 10% who let AI agents create commits without a manual step, to around 40% of devs who no longer personally check the code:

The jump correlates with Opus 4.7 and GPT-5.5 being released, and around the time when many devs seem to have concluded that writing code by hand is dying after experiencing this generation of models' capability at generating code.
Check out the full report from Cursor for more details. Thanks to the team for releasing this data!
Read the full issue of The Pulse this excerpt is from, or check out the latest The Pulse from today. Today's issue covers:
- Bun's Rust rewrite with Fable: what can we learn?
- Anthropic's Fable, OpenAI's GPT-5.6 Sol, Cursor's Grok 4.5, Meta's Muse
- North Korean hackers keep trying to infiltrate full-remote companies
- Industry Pulse: Meta's key logging exposed sensitive data, massive cuts at Xbox, Meta could not buy enough AI capacity from Google, Qualcomm acquires Modular, and memory price hikes hit Apple products.
-
π r/reverseengineering Suspected Russian Threat Actor Impersonates Legitimate Crypto Wallets to Deploy Remote Utilities rss
submitted by /u/CyberMasterV
[link] [comments] -
π @malcat@infosec.exchange In the upcoming 0.9.15 release, Malcat will embed its own 100% native mastodon
In the upcoming 0.9.15 release, Malcat will embed its own 100% native #capa engine. So you get:
- x10 .. up to x100 scan speedup
- command line tool (using headless malcat lib)
- embedded capa rule editor
- additional architectures: arm, mips and even ... python: -
π Barre/ZeroFS v2.0.7 release
Segment reclaim & compaction correctness
169d1d4: Compare the fullFrameLocin the compaction repoint CAS so a rewrite into the same source segment is not reverted to the stale frame.b9d0dd8: Read the segment-reclaim segcount scan at the durable level so a segment is never deleted while its death is still an unflushed in-memory debit.7e9e4e2: Prove reclaim deletes from the durable view under the WAL-off production config.eb4f9e7: Check the durable view too in the segment directory-verify so an unflushed overwrite cannot mask a durably referenced frame from the delete backstop.9a3827c: Keep compaction's gather compressed end to end (verify and AAD-rebind only), cap the round in stored bytes plus per-frame overhead, and fan the batch AEAD out on rayon in both directions.
HA / replication correctness
dcb1e6c: Key the cross-term tail clear on the tail's own epoch, not the heartbeat-advanced fence, so a restarted leader's stale tail cannot replay over the new term's fsync-acked writes.cfae108: Validate takeover replay against a per-batch durable provenance stamp and gate boot on a latest-leader record so neither a stale tail nor an election from silence can regress acked state.b3b6028: Raise the replication decode limit above tonic's 4 MiB default.
Robustness fixes
f2d5a6b: Forward mid-scan iterator errors into theDb::scanstream instead of swallowing them as a clean end-of-range.5b66e77: Reject a write or trim whoseoffset + lengthoverflows u64 asEINVALinstead of wrapping into a request-task panic or a stray unreachable extent.
Refactors & housekeeping
2bdf0e0: Fix clippy warning.0cbf4b3: Splitextent.rsinto anextent/module: read, write, select, reclaim, compact.f67f347: Splitfs/mod.rsinto boot, handle, and per-opops/files with their tests.
Full Changelog :
v2.0.6...v2.0.7 -
π r/reverseengineering CVE-2026-25262 Write-What-Where in Qualcomm Sahara confirmed on Snapdragon 8 Gen 1 (SM8450) β partial Firehose auth bypass rss
submitted by /u/UpsetMinute8770
[link] [comments] -
π Rust Blog Announcing Rust 1.97.0 rss
The Rust team is happy to announce a new version of Rust, 1.97.0. Rust is a programming language empowering everyone to build reliable and efficient software.
If you have a previous version of Rust installed via
rustup, you can get 1.97.0 with:$ rustup update stableIf you don't have it already, you can get
rustupfrom the appropriate page on our website, and check out the detailed release notes for 1.97.0.If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (
rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!What's in 1.97.0 stable
Symbol mangling v0 enabled by default
When Rust is compiled into object files and binaries, each item (functions, statics, etc) must have a globally unique "symbol" identifying it. To avoid conflicts when linking together different Rust programs, Rust mangles the original name of items to include additional context such as the module path, defining crate, generics, and more. Historically, this mangling was based on the Itanium ABI, also (sometimes) used by C++.
The new mangling scheme resolves a number of drawbacks from the previous one:
- Generic parameter instantiations preserve their values, rather than being tracked solely behind a hash
- Inconsistencies: not all parts used the Itanium ABI, meaning that custom demangling was still necessary
Since Rust 1.59, the compiler has supported opting into a Rust-specific mangling scheme via
-Csymbol-mangling-version=v0. Since November 2025, this scheme has been enabled by default on nightly, and 1.97 is now enabling it on stable Rust. The legacy mangling scheme can only be enabled on nightly, and the current plan is to fully remove it.See the previous blog post for more details.
Cargo support for denying warnings
It's common practice to deny warnings in CI. Historically, doing so is typically done through
RUSTFLAGS=-Dwarnings. With Rust 1.97, Cargo controls how warnings interact with build success: either silencing them (viaallowlevel), rendering without failing (default,warn), or denying them (viadeny).As a result of Cargo configuration determining the behavior, using this feature doesn't invalidate the underlying build cache, meaning that it's easy to temporarily opt-in. For example, if warnings are adding unwanted noise while working through fixing errors after a refactor, you can run
CARGO_BUILD_WARNINGS=allow cargo check, temporarily silencing them.In CI, jobs can instead set
CARGO_BUILD_WARNINGS=denyto deny warnings. This can be combined with--keep-goingto collect all errors and warnings rather than stopping on the first failing package.See the documentation for more details.
Linker output no longer hidden by default
rustc invokes a linker on behalf of users. Historically, rustc has silenced linker output by default if the link completes successfully. This can mask real problems, though, so in Rust 1.97 we are enabling linker messages by default. These are emitted as a warning lint, for example:
warning: linker stderr: ignoring deprecated linker optimization setting '1' | = note: `#[warn(linker_messages)]` on by defaultCommon linker messages that have been diagnosed as false positives or intentional behavior are filtered out by rustc. Several defects have already been fixed as a result of no longer hiding this output on nightly.
Note that currently,
linker_messagesis a special lint that is not affected by thewarningslint group. This is intentional as rustc generally doesn't control linker output as precisely, and it's not uncommon for output to only appear on some platforms. If you are seeing what you think is a false positive output from the linker, please file an issue.To silence the warning in the mean time, you can configure the lint level to allow. This can be done through
Cargo.tomlby adding a lints section like this:[lints.rust] linker_messages = "allow"Stabilized APIs
Default for RepeatNCopy for ffi::FromBytesUntilNulErrorSend for std::fs::Fileon UEFI<{integer}>::isolate_highest_one<{integer}>::isolate_lowest_one<{integer}>::highest_one<{integer}>::lowest_one<{uN}>::bit_widthNonZero<{integer}>::isolate_highest_oneNonZero<{integer}>::isolate_lowest_oneNonZero<{integer}>::highest_oneNonZero<{integer}>::lowest_oneNonZero<{uN}>::bit_width
These previously stable APIs are now stable in const contexts:
Other changes
Check out everything that changed in Rust, Cargo, and Clippy.
Contributors to 1.97.0
Many people came together to create Rust 1.97.0. We couldn't have done it without all of you. Thanks!
-
π Console.dev newsletter Davit rss
Description: Native macOS Container GUI.
What we like: Manage Apple Containers via the UI. Open a shell inside any container. Supports volumes, images, networks. Built-in file browser. View container stats and inspect container details. Native macOS app, not Electron.
What we dislike: Not a Docker replacement - itβs Appleβs own implementation. Try OrbStack if you need that (and other things like VMs).
-
π Console.dev newsletter ZeroFS rss
Description: Log-structured filesystem for S3.
What we like: Makes S3-compatible buckets appear as POSIX filesystems or raw block devices. Supports NFS, 9P, NBD. Use ZFS to mirror across regions. Segments are immutable, compressed, encrypted. Local caching. Optional web dashboard and file manager.
What we dislike: AGPL licensed by default, alternative available on a commercial basis.
-
π Ampcode News The Dial rss
Amp's agent modes are now a dial:
low,medium,high,ultra. They replacesmart,deep,rush, andlarge.
The old modes were models in disguise: each name hid a model, a prompt, a reasoning effort β and to pick one, you had to know what that model was like this month. That world is gone. The models converged, open-weight models got seriously good, and the only question left is capability against cost.
The dial asks one question: how hard is this task?
Missing in either direction costs you. Undershoot and the model churns: wrong fix, re-prompt, wrong fix again. You pay three times for a result you could have had once. Overshoot and you're using Fable to fix a typo. Set it right and you pay for exactly the intelligence the task needs.
ultraβ The outcome is clear, but the path is full of unknowns. Migrations, architecture, changes that span many files, systems, and decisions the model has to discover as it goes.highβ You know where the change goes, but getting it right is hard: cross-cutting changes, concurrency, bugs where a subtle miss is expensive. You get diffs closer to reviewer-ready thanmediumgets you β but plan on one round of feedback before merging, and about twice the wait.mediumβ You know roughly what you want. This should be your default. It handles messy, multi-part tasks, fuzzy requirements, the steps you didn't spell out. Strong enough for most work, fast enough to steer.lowβ You know exactly what you want. Bug fixes, tests, refactors, features you can describe precisely. There is less to figure out for the model, solowbuilds it.
Turn the dial with
Ctrl+Sin the CLI, or with the mode picker in the web app.
Under the Hood
We want you to know exactly what you're getting, so here's what backs each mode today. This wiring will change as models improve. The dial won't.
ultra: Claude Fable 5, with a system prompt written for it. GPT-5.6 Sol as the oracle.high: GPT-5.6 Sol atxhighreasoning effort. Claude Fable 5 as the oracle.medium: GPT-5.6 Sol at medium reasoning effort. GPT-5.6 Sol at high effort as the oracle.low: GLM-5.2, Z.ai's open-weight model, the strongest open model on agentic coding. GPT-5.6 Sol as the oracle. (Workspace admins can choose to use GPT-5.6 Terra low instead of GLM-5.2 here.)
Reasoning effort is part of the tier now. No more cycling
Opt+Dthrough effort levels on top of picking a mode.Every mode has an oracle for second opinions. On the top tiers, it's the other frontier model: in
high, GPT-5.6 Sol writes and Fable reviews. Inultra, Fable writes and GPT-5.6 Sol reviews.Migrating
smart,deepβmedium(same model and effort asdeep). Turn up for hard problems.rushβlow.deep**3->ultraorhigh
Want to Tune It Yourself?
The dial removes knobs from the default experience, not from Amp. Plugins can register their own agent modes with your model, your prompt, and your tools, and they show up right next to the built-in ones.
We used that same plugin API to package up the deprecated modes β exact system prompts, exact tool lists, same models and reasoning efforts. If you want
smart,deep,rush, orlargeback, install them:amp plugins add --auto-update @amp/smart-classic amp plugins add --auto-update @amp/deep-classic amp plugins add --auto-update @amp/rush-classic amp plugins add --auto-update @amp/large-classicThen run
plugins: reload(or restart the CLI) and they appear in the mode picker as Smart (classic), Deep (classic), Rush (classic), and Large (classic) β the original names stay reserved for the built-ins.--auto-updatekeeps them current when we update the plugins; drop it if you'd rather pin. The full list of installable modes is on ampcode.com/models.Start at
medium. Turn it down when the task is clear. Turn it up when a miss costs more than the wait.We'll follow up with posts on each mode and numbers on what each one can handle.
-


