🏡


  1. September 21, 2026
    1. 🔗 Simon Willison Jev introduces a new shape of LLM - System One, aka Decision Models rss

      Last week TypeSafe AI unveiled Jev, their first example of a new category of model that they are calling "System One models" (I'm with Maggie Appleton, I think "decision models" is a better name for these). Jev is an interesting variant on the usual LLM format: it still accepts text inputs, but instead of text output it returns floating point numbers corresponding to categories, yes/no questions, ratings, and associated confidence scores.

      TypeSafe describe Jev like this:

      Think of Jev as a frontier-intelligence function call: unstructured state in, typed probabilistic decisions out.

      It's also very fast, and really cheap. Regular LLMs are priced in terms of input and output tokens, with output generally charged at significantly higher rates. Jev charges only for input - output is free - and the input price of their first model is $0.042 per million tokens - cheaper even than OpenAI's GPT-5 Nano ($0.05/million).

      Jev lets you ask questions about text or semi-structured data. You compose a "state" object containing a string, array of strings, or set of name-value pairs - this might describe an article, or a customer, or any other kind of record. You then send that to their API with one or more questions, and get a reply back for each.

      You can ask three kinds of questions:

      • Yes/No questions, which Jev calls "Noul" questions - their CEO confirmed on Hacker News that this is short for Bernoulli, from the Bernoulli distribution. You pose a statement and get back a floating point number between 0 and 1 for how confident the model is that the statement is true.
      • Choice questions, where the model picks one from a set of provided options - actually a confidence score plus a probability distribution across all of the options.
      • Score questions, where you provide sequence of numeric levels with descriptions and it provides a floating point score somewhere along that range.

      The Jev API can accept a single document ("state") and as many questions as you can cram into the context window. Questions are evaluated in parallel, so sending many questions should take a similar time to sending just one.

      I think the decision model framing is useful for understanding where to use Jev. It's great for anything that can be expressed as a classification task - think spam detection, suggesting labels, prioritization and ranking.

      I've also been experimenting with it for search reranking, where you fetch 100 likely matches using an inexpensive algorithm like BM25, then have Jev score those 100 candidates for relevance against the original query.

      Black boxes are back in fashion

      Something I've found a little uncomfortable about Jev is how it very much represents a regression even further towards black box machine learning systems.

      LLMs are black boxes already - you can ask them to justify their decisions, but you can't guarantee that what they say is useful or accurate.

      Jev doesn't even give you that: put in all the text you want, the only thing you're going to get back is a floating point number. If Jev marks something as spam, which content signals tipped it off?

      This also means that concerns about bias should be front and center. I really hope nobody uses Jev to rank job applicants - that floating point number could conceal all manner of unseen bias baked into the models, and experimentally picking that bias apart is going to be a tricky business.

      (I tried one experiment where I had Jev score every city in the San Francisco Bay Area on a yes/no answer to whether they were a "Good city?" - it rated Cupertino top and East Palo Alto bottom. Huh.)

      In practice, this all means that evals and structured experiments are even more important than they are for regular LLM projects. Thankfully, Jev is so cheap that running hundreds or even thousands of experimental prompts through it costs just a few cents.

      Unconventional uses for Jev

      It's been really fun watching the wider community come up with potential use-cases for Jev over the past few days. Here are some creative ones that caught my eye:

      • jevchat by Kyle Pena turns Jev into a (terrible) chat model. "At every step it asks Jev one question: Given the user's question and the reply written so far, which symbol comes next?". ericpruitt on Hacker News: "It's the digital equivalent of Morty speaking with the death crystal".
      • jev-leftpad by Fatih Kadir Akın implements left-pad with the prompt "How many spaces are needed before value to reach targetLength?" and a choice query allowing options from "0 spaces are needed" to "10 spaces are needed".
      • jev-2048 by Andy Gayton uses Jev to play the 2048 sliding puzzle game.

      Open weight recreations

      There's also been a flurry of projects attempting to create a model like Jev using on top of open weight models. Kev is one interesting example, using Qwen 3.5 to produce 0.8B, 4B, and 9B models. Here's the accompanying Hacker News thread, where someone linked to a JevBench benchmark that has already cropped up to compare "Jev-class decision models".

      Given Jev was released just under a week ago, the amount of activity around it is extremely impressive.

      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.

    2. 🔗 idursun/jjui v0.10.11 release

      A point release with bug fixes, in-app Git credential prompts, and visual improvements to Annotation View.

      Features

      • Git credentials: Git credential requests now appear inside jjui, alongside existing SSH askpass support. Usernames remain visible, while passwords and passphrases are masked. Git and SSH askpass support is enabled by default; the old ssh.hijack_askpass setting is replaced by askpass.enabled. Set it to false to disable jjui's credential prompts. (#746)
      • Evolog: Pressing s on a hidden entry in evolog splits the change into two: the original change is restored to the selected historical state, and the later edits become a new child change. This is particularly useful when you forget to start a new change before making unrelated edits, as you can separate them at the point they began without manually selecting files or hunks. The best part is that this feature ships in the default configuration using only Lua actions. (#686)
      • Describe: Press ctrl+x to clear the entire description into the editor's yank buffer, then ctrl+y to restore it. (#749)
      • Details: Press i to invert file selection. (#707)
      • Bookmarks: The interactive bookmark pane now supports ctrl+click to toggle selection and alt+click to select a range. (#742)

      Improvements

      • Annotation View: Leaving with uncopied comments now asks for confirmation. Press esc to keep reviewing, or select Discard to leave. (#744)
      • Annotation View: Improved rendering and contrast so text is easier to read against highlighted backgrounds.
      • Lua: Previously, Lua actions could read application state through the context module, for example with context.change_id(), context.file(), and context.checked_commit_ids(). In this release, I've added the first "read state" function attached to a feature: revisions.inline_describe.content(). It returns the current inline description draft, or nil when the editor is unavailable. It's only implemented for inline describe for now, but I'd like to extend this to the rest of the application.
      • Lua: Existing selection getters now read live UI state so scripts see the current selection after yielding actions and refreshes.
      • Lua: Choice dialogues now show footer help and a visible filter: prompt. (#743)
      • Describe: Pressing esc with unsaved changes now asks for confirmation, replacing the previous draft-stashing behaviour. In the confirmation, enter discards the draft and esc keeps editing. (#523)
      • Command input: The : and $ inputs now appear above the status bar, giving them more room. (#683)

      Bug Fixes

      • Annotation View: Copying annotations now uses the system clipboard. (#741)
      • Annotation View: Pressing esc while help is expanded closes help first. (#744)
      • Annotation View: Opening full-file views correctly handles paths containing spaces.
      • Bookmarks: Moving bookmarks to hidden revisions now correctly targets the selected historical commit. (#748)
      • Help: Expanded help stays within the viewport. (#574)
      • Status bar: Restored footer mode labels.
      • Diff range: Accepting a range without selecting another target now compares the starting revision against the working copy by omitting --to.
      • Terminal: Fixed a crash when the terminal reports an empty background colour. (#745)

      What's Changed

      New Contributors

      Full Changelog : v0.10.10...v0.10.11

    3. 🔗 navidrome/navidrome v0.64.1 - Security Fixes release

      This is a security release. It fixes five vulnerabilities reported through our GitHub Security Advisory program, covering Subsonic authentication, artwork fetching, playlist cover images, player ownership, and per-user library filtering. Upgrade as soon as you can. Thanks to the researchers credited below for reporting them privately.

      The release also improves Jellyfin client support, with both Manet and JellyBox tested and validated against live servers. Manet used to abort its entire library sync on a single missing field and show an empty library. It now syncs end to end. JellyBox got stuck on the login screen. It now signs in and plays, confirmed on Android. Navidrome also reports itself as Jellyfin 12.1.0, accepts Quick Connect sign-in, and can announce itself on your local network so clients find it without you typing an address.

      Smart playlists can reference another playlist by path, and the web UI now formats dates using the language you picked in Personal settings.

      Security

      • Unauthenticated password brute-force through the Subsonic API. Failed Subsonic logins were never throttled, so an attacker could guess passwords at full speed. Navidrome now rate limits failed authentication attempts. High, CVSS 7.4. (GHSA-p994-r776-mw52, #6185) Reported by @osageling.
      • Authenticated SSRF through M3U external album artwork. A playlist could point #EXTALBUMARTURL at a private or loopback address, turning the server into a probe for internal network services. Navidrome now blocks private and loopback addresses in remote image fetches. Medium, CVSS 6.5. (GHSA-8hjf-6h34-82hr, #6181) Reported by @kaardeco.
      • Cross-library file read through the M3U playlist cover. #EXTALBUMARTURL also accepted a local path, so a playlist could serve any file the server can read as its cover image. Only real image files are accepted as local artwork sources now. Medium, CVSS 6.5. (GHSA-vwq6-xrw5-phpg, #6180) Reported by @qrn12580.
      • Player takeover by any authenticated user. Creating a player could overwrite an existing record and reassign its owner, and device registration reused another user's player without an ownership check. Both paths now check the owner. Medium, CVSS 6.4. (GHSA-37h4-53gj-cw8m, #6184) Reported by @RealFakeAccount and @qrn12580.
      • Library filter skipped on bookmarks, playlist tracks and now-playing. These three endpoints ignored the libraries a user is allowed to see, leaking track metadata from other libraries. The filter now applies to all of them. Medium, CVSS 4.3. (GHSA-pcjv-h48m-833g, #6179) Reported by @sondt99.

      Configuration Changes

      Status | Option | Description | Default
      ---|---|---|---
      New | Jellyfin.AutoDiscovery | Answers Jellyfin's UDP discovery broadcasts, so clients find the server on the local network. (#6169) | false
      New | Jellyfin.QuickConnect | Allows Quick Connect sign-in, where a client shows a code you approve from a session that is already signed in. (#6174) | true

      For a complete list of all configuration options, see the Configuration Options documentation.

      Jellyfin API

      • Add Quick Connect sign-in. The client shows a short code, and you approve it from a session that is already signed in, so the client never sees your password. (#6174 by @deluan)
      • Add opt-in LAN auto-discovery, so Jellyfin clients find the server without you typing its address. Docker users need host networking for the UDP broadcast to reach the container. (#6169 by @deluan)
      • Report Jellyfin 12.1.0 and add the 12.x features clients check for, including fillWidth and fillHeight image sizing. (#6163 by @deluan)
      • Match Jellyfin's item payloads, so clients that decode strictly can finish a sync instead of erroring out. (#6151 by @deluan)
      • Match Jellyfin on login SessionInfo, item types and universal streams. (#6161 by @deluan)

      UI

      • Format dates using the language selected in Personal settings, instead of always following the browser locale. (#6160 by @deluan)

      Smart Playlists

      • Reference another playlist by its path in a smart playlist rule, instead of by id. (#5187 by @davidvedvick)

      Subsonic API

      • Log a warning when a nowPlaying scrobble sends more than one id, which the API does not allow. (6b3938b5b by @deluan)

      Server

      • Fix the ExtAuth logout redirect on unauthenticated page loads, and stop the warning spam from untrusted sources. (#6176 by @deluan)
      • Return 404 instead of 500 when a native API resource does not exist. (#6131 by @deluan)

      Artwork

      • Report a failure when the Last.fm artist page has no image. Last.fm now answers non-browser clients with a bot challenge page, which Navidrome read as "this artist has no image" and recorded as final, with nothing in the log. It now logs a warning and retries, and the other image agents still get their turn. (#6198 by @deluan)
      • Store artwork files as group-readable (mode 0640) instead of owner-only, so other services on the same host can read the image cache. (#6189 by @kwo)

      Scanner

      • Update go-taglib to fix "permission denied" errors on shared hosts. (d00c84716 by @deluan)

      Scrobbling

      • Double-encode plus signs in artist and track names sent to Last.fm, so tracks with a + in the name scrobble correctly. (#6158 by @deluan)

      Packaging

      • Repair root-owned artwork and plugins folders on upgrade. Installs affected by this could not write their own cache. (#6143 by @deluan)

      Translations

      New Contributors

      Full Changelog : v0.64.0...v0.64.1

      Helping out

      This release is only possible thanks to the support of some awesome people!

      Want to be one of them?
      You can sponsor, pay me a Ko- fi, or contribute with code.

      Where to go next?

    4. 🔗 smol-machines/smolvm smolvm v1.17.0 release

      What's Changed

      • Let aarch64 Linux resume a branch source instead of freezing it by @BinSquare in #1327
      • Attach host disks and vhost-user block devices to a machine by @BinSquare in #1326
      • agent: refresh persistent DNS and retain shutdown receipts by @sgrove in #1328
      • Return a directory listing when the files API is asked for a directory by @BinSquare in #1330
      • Fail a delete that needs confirmation when stdin is not a terminal, instead of reading EOF as a decline and exiting successfully by @BinSquare in #1333
      • Run the image's own entrypoint for a cached --oci-cache run instead of the bake's no-op placeholder by @BinSquare in #1335
      • Provision the --oci-cache bake without launching a workload so images without /bin/true can be cached by @BinSquare in #1340
      • Give a clone a host port the kernel will not reassign before it binds by @BinSquare in #1341
      • Rebuild libkrun so aarch64 machines can branch again by @BinSquare in #1342
      • Make incremental checkpoints reusable as a Rust crate by @BinSquare in #1344
      • Reserve every recorded host port so a clone is never given a stopped machine's port by @BinSquare in #1345
      • Forward CLI --secret-env/--secret-file secrets to the workload on the oci-cache and pack-ref run paths by @BinSquare in #1343
      • Save checkpoints without staging a second RAM copy by @BinSquare in #1305

      New Contributors

      Full Changelog : v1.16.2...v1.17.0

    5. 🔗 gildas-lormeau/single-file-cli v2.15.4 release

      SingleFile CLI 2.15.4

      Changes

      • single-file-core is updated to 1.6.9, see https://github.com/gildas-lormeau/single-file-core/releases/tag/v1.6.9. For the CLI it means that a rule the capturing browser rejects, because one selector of its list is unsupported there, no longer hides a rule that browser actually draws: with --browser-engine firefox the lists of a shared Gemini conversation kept their indent, where they used to fall back to the browser default

      Co-authored by Claude (Claude Code)

    6. 🔗 MetaBrainz MusicBrainz Server update, 2026-09-21 rss

      Hi! It's been a while since our last release since we have been working on stability improvements for both website and search to better cope with all the load we are handling recently. The first related changes are part of this release, with more to come, including limiting searches to 500 results (if you need something further down the search, sorry but you probably need a better search!). Additionally, the very annoying bug that sometimes lost track times when parsing tracklists should hopefully be gone now (thanks dvirtz!), and a lot more aggregator and shortener links are now blocked; even when not yet blocked, remember to always add all the relevant destination links rather than redirects and aggregators if possible.

      A new release of MusicBrainz Docker is also available that matches this update of MusicBrainz Server. See the release notes for update instructions.

      Thanks to derat, dvirtz, ibmibmibm and mib for having contributed to the code. Thanks to DenizC, derat, dvirtz, HibiscusKazeneko, j.rohr, outsidecontext, Raman Sinclair, rinsuki and salo.rock for having reported bugs and suggested improvements. Thanks to AligFu, AndrejsD1718, BestSteve, blueday, Covium, Denatura, EmO686, Flavia Telcean, joao_over9k, Kolesteraw, Life4649, liilliil, mfmeulenbelt, naturbrilian, NorwayFun, Priit Jõerüüt, pXF, syntariavoxmortem, TheParaziT, Vaclovas Intas, vacuousVersifier and wileyfoxyx for updating the translations. And thanks to all others who tested the beta version!

      The git tag is v-2026-09-21.0.

      Fixed Bug

      • [MBS-9526] - Parser removes times, despite "use track times" being unchecked
      • [MBS-10767] - "more" and "less" on rel types list are not translatable
      • [MBS-14386] - Series of series doesn't show parts as a list, only on relationships section
      • [MBS-14398] - Collection checkbox in header doesn't work
      • [MBS-14440] - Webservice requests can return authenticated data on unauthenticated requests
      • [MBS-14448] - Memory leak in Data::Relationship::_new_from_row

      Improvement

      • [MBS-14192] - Require visiting tracklist tab when adding release
      • [MBS-14399] - Accept new /a LibraryThing author URLs
      • [MBS-14404] - Reject Facebook "share" URLs
      • [MBS-14415] - Strip locale and mibextid in Facebook URL cleanup
      • [MBS-14423] - Reject Google "share" URLs
      • [MBS-14424] - Block Pinterest URL shortener
      • [MBS-14439] - Block (yet) more smart links
      • [MBS-14425] - Block smart links: drum.io
      • [MBS-14426] - Block smart links: ffm.bio
      • [MBS-14427] - Block smart links: social.tunecore.com
      • [MBS-14428] - Block smart links: frontl.ink
      • [MBS-14429] - Block smart links: gyro.to
      • [MBS-14430] - Block smart links: paa.ge
      • [MBS-14432] - Block smart links: linkin.bio
      • [MBS-14433] - Block smart links: beacons.ai
      • [MBS-14435] - Block smart links: fanbase.to
      • [MBS-14436] - Block smart links: soundon.global
      • [MBS-14437] - Block smart links: imusician.pro
      • [MBS-14438] - Block smart links: musics.to
      • [MBS-14443] - Support Boomplay’s new non-numeric URL format
      • [MBS-14450] - Improve error / rejection messages for URL shorteners and aggregators
      • [MBS-14455] - Limit the depth of search to 500 results

      Task

      • [MBS-14382] - Update the Amazon logo used in the sidebar
    7. 🔗 pydantic/monty v1.0.0-beta.2 - 2026-09-21 release

      What's Changed

      Full Changelog : v1.0.0-beta.1...v1.0.0-beta.2

    8. 🔗 microsoft/markitdown Version 0.1.8 release

      This release rolls up dozens of small patches and bug fixes. For typical inputs and use cases, we expect outputs and behavior to remain largely unchanged from version 0.1.7.

      The markitdown-ocr plugin has also been refactored to simplify future maintenance.


      What's Changed

      New Contributors

      Full Changelog : v0.1.7...v0.1.8

    9. 🔗 gildas-lormeau/single-file-cli v2.15.3 release

      SingleFile CLI 2.15.3

      CLI fixes and improvements

      • A JavaScript dialog opened by the page no longer stalls the capture. The browser stops the page until a dialog is answered and the CLI answered none, so an alert() in an inline script ended in "Load timeout" with no file, and one fired after load hung the process past every timeout. The dialog is now dismissed as soon as the browser reports it and the page runs on as if the user had closed it: confirm() returns false and prompt() null. A beforeunload dialog is accepted so the navigation proceeds
      • When a page stops answering during load, the fallback that stops the load and captures what is there is now bounded by the capture timeout instead of waiting for ever

      Changes

      • single-file-core is updated to 1.6.8, see https://github.com/gildas-lormeau/single-file-core/releases/tag/v1.6.8. For the CLI it means that a rule written directly inside an @scope block with a selector starting with a combinator is no longer removed as unused, which restored the spacing of shared Gemini conversations; that the onbegin, onend and onrepeat handlers of SVG animation elements are removed with the other event handlers when scripts are blocked; and that the infobar's expanding ring no longer replays each time the infobar is folded back

      Co-authored by Claude (Claude Code)

    10. 🔗 earendil-works/pi v0.87.0 release

      New Features

      • Canonical session context and extension boundaries — Edit model context without rewriting history and add actionable lifecycle hooks. See ContextEditEntry and extension events.
      • Full-transcript context extensions — Use context_with_system for per-request system-message transformations. See context_with_system.
      • Per-model image input limits — Configure cache-safe image resizing per model for attachments, read, and tool-result images. See Image Input Limits.

      Breaking Changes

      • Removed the inherited shouldStopAfterTurn agent option. Use finishTurn and return { action: "end" } instead. finishTurn runs before turn_end but applies the decision afterward, and it also receives error and aborted responses; migrate normal-response predicates by returning undefined for those hard exits. See the @earendil-works/pi-agent-core changelog for a complete before-and-after example.
      • Added ContextEditEntry to the exported SessionEntry union. TypeScript consumers with exhaustive entry switches must handle context_edit; use replacement: null for omission and a content replacement otherwise.
      • Made SessionManager canonical for AgentSession provider context. Assigning session.agent.state.messages no longer replaces future request history; restore with SessionManager.inMemory(cwd, { id }, entries), navigate with session.navigateTree(), or append through session.sessionManager and call session.refreshContext().
      • Expanded TurnEndEvent with required boundary fields and added AgentBeforeSettleEvent to the exported ExtensionEvent union. Consumers constructing events or exhaustively switching on ExtensionEvent must handle the new shapes. ExtensionRunner.emit() no longer accepts turn_end; host integrations dispatch actionable boundaries with emitBoundary(baseEvent, buildContext).
      • Deferred runs requested from agent_settled handlers until all settled handlers finish. Handlers still observe ctx.isIdle() === true, but no longer see a reentrant agent_start during the same notification dispatch.

      Added

      • Added append-only model-context edits. For example, sessionManager.appendContextEdit(entryId, null) omits one message from future provider context without changing raw history, usage, or UI history.
      • Added actionable turn_end and agent_before_settle extension boundaries. Return { entries: [...event.entries, draft], continue: true } to persist structural entries in order and ensure one next provider request without changing steering or follow-up scheduling.
      • Added retain-none compaction input: sessionManager.appendCompaction(summary, null, tokensBefore) stores the compaction's own ID as its kept boundary.
      • Added the context_with_system extension event, which runs after context handlers on the full transcript including system messages and sends its result verbatim. See context_with_system.
      • Added per-model image resize profiles through inputLimits.images.resize in models.json, applied to file attachments, image reads, and tool-result images (#9631).

      Fixed

      • Fixed string context-edit replacements producing invalid assistant and tool-result message content instead of text blocks.
      • Fixed context-invisible boundary metadata and replacement edits causing newly appended or replaced input to be summarized before its first provider request.
      • Fixed edited-context accounting both discarding valid assistant usage captured after the latest context edit and reusing that usage after a later compaction made it stale.
      • Fixed selected error retries and final length/overflow recovery retaining abandoned model attempts in future provider context; post-run recovery omissions are now persisted without hiding raw transcript history or changing queue scheduling.
      • Fixed context handlers that filter or slice messages dropping the prompt and tool declarations, which after extension-driven compaction left requests without built-in tools or made Codex emit raw tool-call text. Handlers no longer see system messages; Pi restores the prompt and tool state after they run. See context (#9789, #9822).
      • Fixed /bug allowing uploads in offline mode while preserving local zip exports (#9841 by @christianklotz).
      • Fixed idle prompt-cache warming rebuilding expired caches when its timer or an extension decision is delayed.
      • Improved crash diagnostics with hints identifying loaded extensions that appear in the stack trace.
      • Fixed text files beginning with GIF being misclassified as images and omitted from read and CLI @file input (#9755).
      • Fixed malformed prompt template frontmatter being silently ignored instead of reported as a resource warning (#9830 by @christianklotz).
      • Fixed inherited unknown OpenAI-compatible Chat Completions endpoints receiving strict tool schemas unless they explicitly advertise support (#9816).
    11. 🔗 pydantic/monty v1.0.0-beta.1 - 2026-09-21 release

      What's Changed

      New Contributors

      Full Changelog : v0.0.23...v1.0.0-beta.1

    12. 🔗 r/LocalLLaMA How it feels watching prices go up rss

      How it feels watching prices go up | submitted by /u/Hyacin75
      [link] [comments]
      ---|---

    13. 🔗 exe.dev Caring vs. Knowing rss

      A few months ago, I wrote about how AI is disrupting the build-vs-buy equation in SaaS. I argued that the real value of SaaS over most DIY software is knowing what good looks like. But a few recent events have made me realize that knowing in and of itself is not enough.

      “Good” changes. Users evolve, surrounding systems shift, and expectations rise. And the rate of change somehow continues to increase, making us feel the technological jerk of products in our daily life. Building something valuable requires knowing what good looks like today. Maintaining (or even increasing) that value requires caring enough to keep learning what good will look like tomorrow.

      In a conversation with Betty Junod on my podcast Third Loop, we discussed the idea of an application with an Ideal Customer Profile, or ICP, of one. Betty’s point was that the cost reduction that comes from an agent building your app makes it reasonable to build an app that only you will use. This is liberating for people who have an idea or need, but previously lacked the coding skill or resources to make a computer do things they considered useful.

      If you are the ideal customer, you know what good looks like and understand the constraints because you are the only user. But what happens when you’re building for more than just one customer? The challenge is the same whether you are building an app to store your recipes or a service to monitor VM utilization. As the number of users increases, answering what good looks like becomes more challenging. Humans have the amazing ability to solve the same challenge with incredible variety. Your definition of good may vary slightly from your next user’s. As the number of users grows to hundreds or thousands, the variations—and resulting complexity—can multiply rapidly.*

      *Yes, humans can use em dashes appropriately.

      Choose Your Own Adventure

      In the old world, this is where DIY often broke down. You’d add personalization or customization, but it had a cost, both in the building as well as maintaining the increasing complexity of a system. For many SaaS companies, this led to narrowly scoping the ICP and then expanding features over time to meet the needs of more people.

      This approach was sustainable for the SaaS provider, but meant that users had to conform to the provider’s view of the workflow. It also meant that providers built new features against rigid, explicit user stories and happy- path workflows.

      In this new world of free code, what if we could let the user build the experience they wanted? Give the user access to the agents that build the features. This starts to change the way we think about designing products. We may need to think more about designing primitives and building blocks and not just a single fixed user path.

      Of course, as we look to acknowledge that each user is a snowflake we quickly realize that different users care about different things. A default setting for one user may be appreciated, while for another it is a deal-breaker that ruins their experience. Some users want a product to make all the choices for them, while others wish they could have control down to the bit level for every interaction.

      Put another way, sometimes you want to buy a pre-made sandwich, and sometimes you want to bake your own bread from the wheat you harvested and milled yourself. And most people, most of the time, are somewhere in between. We are finally at a point where we can build for—and with—users across this spectrum.

      So, as we build our choose-your-own-adventure platforms, selecting good defaults and caring about what good looks like over time is what keeps a growing user base happy. It’s great to have a computer make all the choices for you when they are the choices that you want. But building a system that makes all the right choices remains aspirational. For now, we can try to build systems that adapt to our personal “right” choices faster than we have in the past.

      Ever-Changing “Good”

      Another critical aspect of this is that “good” can change over time. In 1440, when Gutenberg made the first printing press, his list of requirements was a bit different from the last laser printer I purchased from Costco. The rate of change that is acceptable to the user is also a factor. If your ICP is slower to adapt to change, either by comfort or regulation, you need to plan accordingly.

      How do you ensure that your product or service continues to be good? How do you monitor for drift—either in your product quality or in your ICP needs? Product quality isn’t just your uptime. Users rarely use products in a vacuum, especially SaaS. Other services provide input and users need the output to feed into other places. And the needs of these inputs and outputs are changing faster today than ever before.

      At the end of the day, knowing what good looks like is a point-in-time judgment, while caring about what good looks like is an ongoing task. You have to spend effort observing and processing usage patterns and feedback. Even as we build a platform that can be augmented and updated by our users, we still have to observe and listen to both new and existing users and incorporate learnings into our design and build process. This means your product is never “done” or “finished.” It also means that, as a builder, you may have to let go of the idea that your product will be used the way you intended.

      Free, Like Puppies

      Puppies are not free. Food, toys, vet visits, and time all add up, regardless of the initial cost. This has long been a comparison used for open source software. And it needs to be acknowledged that unrestricted customization can have the same risk.

      In this new era of “I can build anything,” this often means you first have to make a choice, “do I care enough about what good looks like for this product to own the maintenance and upkeep?” This isn’t just about upgrading, patching, CVEs, and performance (although that is a big part of it). Handing users the controls to change your product also runs the risk that they’ll make changes they regret. Or, when the choice is good for the user, it may restrict your optionality in the future if desired core product changes break their customization.

      Different types of people have different tolerances for build-vs-buy. There are people like Josh, who look at the world and say, "I could build that myself," and then do. Or people like my brother who will pay for other people to build everything. And then some who tinker in between. Either way, someone still has to feed the puppy.

    14. 🔗 r/LocalLLaMA 16GB (and in many cases 12GB) is the max vram most people will ever reasonably have rss

      This sub is, needless to say very niche and skewed towards the high end. There are tons of extremely high end setups here with multiple gpu's etc.

      Even 24GB is out of reach of most people financially, forget about the 3x3090 or 5090 or even higher setups. Macs/Strix Halo/dgspark etc are all similarly expensive. 16GB is pretty much the high end for most. And this completely changes in most of the rest of the world where even 12GB would be a luxury.

      Things have changed recently (I think even last 6 months have been huge) and even agentic coding is now feasible on 16GB cards (eg with Qwen 27B quants).

      I think/hope things will continue to improve. Of course there's going to be a hard limit on how much world knowledge these smaller models will have.

      The holy grail is new architecture that supercedes the Transformer and new techniques that don't depend on vram/bandwidth.

      and

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

    15. 🔗 HexRaysSA/plugin-repository commits sync repo: +3 releases rss
      sync repo: +3 releases
      
      ## New releases
      - [clang-include](https://github.com/oxikkk/ida-clang-include): 1.3.0
      - [haruspex](https://github.com/0xdea/haruspex): 0.10.1
      - [rhabdomancer](https://github.com/0xdea/rhabdomancer): 0.10.1
      
    16. 🔗 Project Zero Windows Exploitation Techniques: Dangling COM Object Registrations rss

      This short blog post is about abusing a privilege escalation bug that Microsoft recently fixed in Windows, CVE-2026-66804, that I and 14 others reported. This issue is an incomplete fix for CVE-2026-50343, a bug dubbed “Dark Elevator” by Calif.

      The root cause of the bug was a dangling COM object registration for the CrossDevice COM object with the CLSID {E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496}. A COM registration typically needs two parts: a server executable, which for in-process components is a DLL and a CLSID entry under the HKEY_CLASSES_ROOT registry key which points to that DLL.

      This object was registered in the system wide classes key, meaning it was accessible to all users on the system, including system services. However the server executable was missing. Specifically it was registered to use the DLL %PROGRAMDATA%\CrossDevice\CrossDevice.Streaming.Source.dll. Not only does this path not exist, it’s also within the C:\ProgramData directory. This is a common location for all users on the system and therefore permits anyone to create directories. Therefore you can create an arbitrary DLL file at that location and the COM object can be instantiated potentially leading to privilege escalation.

      But how to get the COM object, and thus the DLL, loaded into a privileged process? The fixed bug Calif blogged about, CVE-2026-50343, abused a weak registry key permissions to add the class as a installer plugin and then get the InstallService to load it into memory. The issue with the InstallService was fixed, so we need an alternative way to abuse the unfixed dangling COM reference.

      Abuse Custom COM Marshaling, Again

      A technique I’ve used multiple times in the past to load an arbitrary DLL into a privileged process is to abuse custom COM marshaling. When you call an interface method which is implemented out-of-process, the COM runtime will marshal the parameters into an RPC call to send to the server. If a parameter is a COM object then the runtime marshals that object into an OBJREF structure that allows the object to be used in the server. The two main types of OBJREFs are shown in the diagram below, or you can read about them in the official DCOM documentation here:

      The default COM marshaling strategy is by reference which produces a Standard OBJREF containing all the information needed to connect to the original object. The object might even be on a completely different computer. When the object is unmarshaled this information is used to create an RPC channel back to the caller so that the server can call methods on the object.

      The runtime also supports an opt-in marshal by value mechanism if the object implements the IMarshal interface. This allows the object to specify an arbitrary CLSID to use as the unmarshaling object, which doesn’t have to be the same as the object being passed in. When the object is unmarshaled in the server the CLSID is used to lookup an in-process server DLL to load.

      Therefore an obvious technique to exploit the dangling COM object registration is to send a Custom OBJREF to a privileged COM service specifying the CLSID of the dangling object. When unmarshaled, which happens automatically in the runtime before the target method is called, the malicious DLL will be loaded and we’d get privilege escalation. The following code shows how trivial it is to specify the dangling COM class in an IMarshal implementation:

      class FakeMarshal : public IMarshal {
          // Inherited via IMarshal
          HRESULT GetUnmarshalClass(REFIID riid, void* pv, 
                                    DWORD dwDestContext, void* pvDestContext, 
                                    DWORD mshlflags, CLSID* pCid) override
          {
              return CLSIDFromString(L"{E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496}", pCid);
          }
          // ...
      };
      

      We need to find a privileged service to send the marshaled COM object to become an administrator. Unfortunately, finding such a service isn’t so simple. The fact that a custom marshaling object will cause an arbitrary DLL to be loaded into the process and code executed is a risky operation, especially across privilege boundaries. Therefore Microsoft implemented a mitigation which can be enabled to disable custom marshaling in the process unless the class is explicitly opted in, or is one of a small number of trusted components such as classes in the runtime library.

      Since Windows 8 this mitigation is implemented through two mechanisms, the first and original method is setting the EOAC_NO_CUSTOM_MARSHAL capabilities flag when calling CoInitializeSecurity. The second, added to improve security in AppContainer sandboxes is set through the IGlobalOptions::Set method and specifying the COMGLB_UNMARSHALING_POLICY property type. As we’re not trying to escape from a sandbox the only value of importance is COMGLB_UNMARSHALING_POLICY_STRONG which disables custom marshaling similar to the capabilities flag.

      As the dangling COM object isn’t registered as a trusted marshaler this means we need to find a privileged COM server that doesn’t enable these mitigations. The easiest approach is to scan the processes at runtime. The capability flags are stored in the value combase!gCapabilities while the marshaling policy is stored in combase!g_GLBOPT_UnmarshalingPolicy.

      However, I kept thinking there must be a COM service that runs as SYSTEM and doesn’t enable custom marshaling. After a bit of fiddling I found one, although there’s no doubt others. It turned out to be a COM service I’ve researched and exploited before, the Shell Create Object Handler object. This is an interesting COM object, in that while it runs in a SYSTEM service, it’s not directly instantiable:

      PS> $cls = Get-ComClass -Clsid 135fd325-45b7-4c30-89f8-4386961669f0
      PS> $o = New-ComObject -Class $cls
      Exception calling "CreateInstanceAsObject" with "3" argument(s): "Class not registered"
      
      PS> $cls.AppIdEntry | Select Name, RunAs, IsService
      Name                        RunAs               IsService
      ----                        -----               ---------
      Shell Create Object Handler nt authority\system     False
      

      Normally, when a COM object is hosted by a privileged service, it’s registered with the name of a system service that RPCSS will start automatically when the object class is requested. However, in this case as there’s no service,creating the object fails with a “Class not registered” error. In order to create the COM server, the service needs to already be running as the SYSTEM user before you call CoCreateInstance.

      Instead you have to start the privileged server via the \Microsoft\Windows\Shell\CreateObjectTask scheduled task. Fortunately this task can be started by normal users, which you can verify with my Get- AccessibleScheduledTask command:

      PS> Get-AccessibleScheduledTask -Executable | 
               ? Name -Match Shell\\CreateObjectTask
      TokenId  Access                     Name
      -------  ------                     ----
      77E3156D GenericExecute|GenericRead ...\Shell\CreateObjectTask
      

      Of course just starting this task is not enough, you also need to create a global named event, ShellCreateObjectTaskReadyEvent otherwise the task will immediately exit and not export the COM service. A simple script to create an instance is shown below:

      PS> $ev = New-NtEvent -Win32Path "Global\ShellCreateObjectTaskReadyEvent" -InitialState $false
      PS> Start-ScheduledTask -TaskPath "\Microsoft\Windows\Shell\" -TaskName "CreateObjectTask"
      PS> $ev.Wait()
      PS> $o = New-ComObject -Clsid "135fd325-45b7-4c30-89f8-4386961669f0"
      PS> $o
      InterfaceName Iid
      ------------- ---
      IUnknown      00000000-0000-0000-c000-000000000046
      

      You can verify that the object is hosted in a privileged process with the Get-ComProcess command and checking the CustomMarshalAllowed property. Note this command is currently broken on Windows 11 25H2 due to changing structures that I’ve not had a chance to update, it still works on previous versions.

      PS> $objref = Get-ComObjRef -Object $o
      PS> $p = Get-ComProcess -ProcessId $objref.ProcessId
      PS> $p | Select Name, User, CustomMarshalAllowed
      Name    User                CustomMarshalAllowed
      ----    ----                --------------------
      dllhost NT AUTHORITY\SYSTEM                 True
      

      At this point we have everything we need to exploit the dangling COM object, we’ve got a COM service running as SYSTEM with custom marshaling allowed. We can use the CoGetInstanceFromIStorage API to create the object, passing the “fake” marshaled object as the pstg parameter. This object will get marshaled to the COM server process and then unmarshaled unconditionally during object activation. We do need to implement a fake IStorage interface to get it past the local API implementation, which isn’t that difficult but I thought I’d see if there’s an easier way. Let’s look at the supported interfaces:

      PS> Get-ComInterface -Object $o
      
      Name                 IID               HasProxy   HasTypeLib     
      ----                 ---               --------   ----------     
      IUnknown             00000000-0000-... False      False          
      IMarshal             00000003-0000-... False      False          
      IMarshal2            000001cf-0000-... False      False          
      ICreateObject        75121952-e0d0-... True       False
      
      PS> Get-ComInterface -Name ICreateObject | ConvertTo-ComSourceCode -Parse
      [
        object,
        uuid(75121952-E0D0-43E5-9380-1D80483ACF72),
      ]
      interface ICreateObject : IUnknown {
          HRESULT Proc3([in] GUID* p0, [in] IUnknown* p1, 
                        [in] GUID* p2, [out, iid_is(p2)] IUnknown** p3);
      }
      

      The COM object only has one unique interface, ICreateObject. Converting the interface proxy to IDL shows that it takes an IUnknown pointer as its second parameter. Therefore to exploit the dangling COM registration we can just pass the “fake” marshaled object to this parameter and get privileged code execution. I’ve attached an updated, fully working exploit of the bug to the original issue here.

      It’s worth noting that while this exploitation technique makes it easy to exploit dangling COM registrations, it can also be used to exploit buggy COM class custom unmarshalers. Sometimes, just the act of loading a DLL into a process can cause a crash.

      Finding the Original Dangling COM Object Registration

      As a footnote, a quick way to try and find other dangling COM servers would be to use the following PowerShell script with my OleViewDotNet and NtObjectManager modules installed:

      function Test-ComServer {
          param($Server)
          try {
              Use-NtObject($lib = Import-Win32Module -Path $Server -Flags AsDataFile) {
                  $true
              }
          } catch {
              $false
          }
      }
      
      PS> $db = Get-ComDatabase -LoadMode MachineOnly
      PS> $cs = Get-ComClass -Database $db -ServerType InProcServer32
      PS> $cs | ? { -not (Test-ComServer $_.DefaultServer) } | 
              Sort DefaultServer | Select Name, DefaultServer
      

      This will print out any in-process COM class from the machine hive where LoadLibrary can’t find the DLL. It’s important to use LoadLibrary via the Import-Win32Module command as some of the COM registrations only specify the file name and you want to ensure these are resolved correctly according to the system path.

      This script will find the dangling CrossDevice COM class on an unpatched system. Note, you’ll need to manually inspect the paths to see if a DLL can be planted at that location. You could make it smarter by checking if the path is in a directory that can be written to, or even test if an existing DLL can be modified, but that’s an exercise for the reader.

    17. 🔗 r/LocalLLaMA Clarification on the Qwen-image-2.1 license rss
    18. 🔗 r/LocalLLaMA ZCode is now open source rss

      ZCode is now open source | ZCode is now open source , and the reported security issues have been addressed. Source code: https://github.com/zai-org/ZCode The repo includes its desktop app, web workspace, backend, Agent CLI, and runtime. Official announcement: In response to the ZCode product security issues reported by the community, we have completed the necessary remediation and sincerely apologize to all our users. We have open-sourced ZCode at github.com/zai-org/ZCode, placing the code under community scrutiny and making ZCode more open and transparent. We sincerely thank the community developers who previously identified issues in ZCode. Going forward, we will establish an ongoing product security vulnerability reporting and response process. We welcome developers to continue reviewing ZCode and reporting potential issues, and we will provide rewards based on the severity of the issues reported. With respect to the code data referenced by the community, we confirm that no such data is retained and that it has never been used for model training. Following the remediation, we invited the China Academy of Information and Communications Technology (CAICT) and NSFOCUS to conduct security assessments. The results are as follows: Through its technical assessment, CAICT confirmed that the zcode-prod Alibaba Cloud OSS bucket is in a zero-data state. Security remediation has been completed in the ZCode v3.14.0 client. The Repo Wiki feature has been removed, and the workflow for generating and uploading local repository snapshots has been disabled. NSFOCUS confirmed that all data objects in the zcode-prod Alibaba Cloud OSS bucket, as well as the bucket itself, have been deleted. Remediation has been completed in the ZCode v3.14.0 client. The Repo Wiki entry point and the associated generation workflow have been removed, and no functional path capable of triggering the generation of local repository snapshots or transmitting local files externally was identified. Once again, we sincerely apologize and welcome continued scrutiny from the community. The full security assessment report will be released soon. submitted by /u/ResearchCrafty1804
      [link] [comments]
      ---|---

    19. 🔗 Rust Blog GitHub Actions leaking secrets when Miri output is cached rss

      The Rust Security Response Team was notified that Miri stores all environment variables to target/, allowing secrets to persist in caches.

      While not necessary a vulnerability in and of itself, when paired with GitHub Actions caching behavior, it is possible for this to expose secrets to PRs.

      Overview

      GitHub Actions makes it possible to cache directories between runs. Typical setups allow CI runs on main (and other branches) to write to cache, and PRs can only read from cache (preventing cache poisoning). Rust projects tend to speed up CI by caching binaries built by cargo install and sometimes the contents of target/.

      PR CI can be triggered by anyone who can open PRs on your repository. GitHub requires maintainer approval for the first PR, but future PRs will rerun CI on every push. Anyone who has previously landed a change can trigger a CI run extracting information from cached target/ and then cover their tracks by pushing a second commit to the PR.

      GitHub sometimes hides overwritten commits in its UI, making this kind of attack harder to detect. CI run logs and overwritten commits are also deleted after a few months.

      When cargo miri is invoked, Miri needs to retain build-relevant environment variables between runs1. The current code to do so achieves this by storing all environment variables to target/. This, of course, persists when target/ is cached.

      If your environment contained secrets, these can now be accessed by PRs via the cache.

      Our fix

      Our short term fix for this is to make Miri only preserve CARGO_* environment variables (excepting CARGO_*_TOKEN) and OUT_DIR. In the longer term, Miri and cargo may figure out better ways to inform Miri of the relevant list of environment variables. Note that this patch may not be available on nightly yet.

      We also performed an ecosystem scan of GitHub repositories and identified 1 repository with this issue and 7 repositories that do not appear to be vulnerable but should be cautious anyway. We have reached out to those maintainers.

      Am I affected?

      It is likely that our scan was imperfect, so we recommend you check your own GitHub Actions setups if you run Miri.

      You are vulnerable if:

      • You run cargo miri in CI
      • The step that runs cargo miri has access to secrets as an environment variable:
        • By being passed in to the step itself as an environment variable
        • By being set in env for the workflow
        • By being passed in to a previous step that persists it in the environment somehow
      • The workflow being used caches the target directory, usually done via actions/cache or swatinem/rust-cache
      • The cache is accessible to PRs (common and often the intended use case)

      Possible quick fixes include:

      • Disabling cache for that job.
      • Scoping secrets to steps in that job that do not call Miri.
      • Temporarily disabling Miri.

      Once done, please clear the cache. Consider rotating any secrets that might have leaked.

      The Miri release in the upcoming nightly (2026-09-22) will no longer have this problem.

      Even if you do not run Miri, ensure jobs that can write to public caches do not have access to secrets. Many tools do not have special handling for secrets, and assume the entire environment can be written to the filesystem.

      Threat model

      We consider it bad practice to have a cache that can easily be tainted by secrets.

      If caching target/, it is worth making sure that the inputs to processes that create target/ (anything invoking cargo) do not have secrets available. It is generally rare for standard cargo build/test subcommands to need any secrets or tokens2, so this is mostly a matter of being careful about having secrets exposed as environment variables to the entire job.

      Cargo/Miri/Rust does not guarantee that environment variables will be safe from being copied into target/. While we are treating this as a security issue and patching it out of an abundance of caution, this is not something you should rely on in general. Beyond official Rust tooling, it is possible for build scripts to be doing things that lead to the environment being stored in compilation artifacts.

      Acknowledgements

      Thanks to Predrag Gruevski of OpenAI for reporting this issue to us. Furthermore, the ecosystem scan was performed using Codex access and credits donated by OpenAI, which we also thank them for.

      Issue triage and remediation was performed by Manish Goregaokar, Ralf Jung, Ben Kimock, Weihang Lo, Jacob Finkelman, Walter Pearce, Josh Stone, and Mark Rousskov.

      1. Miri is invoked multiple times by cargo miri for complicated reasons

      2. In theory it could come up with build scripts reading from the network

  2. September 20, 2026
    1. 🔗 r/LocalLLaMA Qwen3.8-Flash-Next Cosmic Arcade oneshot slop game rss

      Qwen3.8-Flash-Next Cosmic Arcade oneshot slop game | To test what it can do. Qwen3.8-Flash-Next Intel Autoround W4A16 running locally on 4xV620 ~2k prefill and 70ts decode.. Were running around 3 hours. Harness is OMP (I think it made a big difference). Most of the time model was running 2 browsers simultaneously and testing/fixing everything. The most sloppy prompt possible:

      create a game where a space traveller in the space he neets eniemes who shoots in him and asteroids which he should avoid. he have a blaster gun to shoot enemies and asteroid. space traveller in scafandr and fyoing on the rocket. game should be very lifelike detailed and done with html and js (use any lib you want). 3d game photorealistic. ofc run the browser to debug and fix stuff always
      

      submitted by /u/Thin_Pollution8843
      [link] [comments]
      ---|---

    2. 🔗 r/LocalLLaMA Lawsuit says Anthropic, OpenAI, SpaceXAI and Google made illegal agreement on AI slowdown rss
    3. 🔗 r/LocalLLaMA Qwen-Image-2.1 released! rss

      Qwen-Image-2.1 released! | Meet Qwen-Image-2.1, the most balanced and cost-effective image generation model in the Qwen-Image series! Now open weights! 🎨 A unified model for both generation and editing, delivering top-tier quality in a lightweight package. Highlights: - Compact & exceptionally fast: A lightweight 7B architecture that outperforms most closed-source models, with drastically accelerated inference for multi-image inputs. - Native transparency: Natively generates and edits RGBA layers, enabling seamless compositing and text editing within transparent images. - Versatile, high-fidelity editing: Supports up to 10 reference images and precise local control while preserving strict fidelity for portraits and products. - Broad coverage & stunning aesthetics: Excels at panoramas, infographics, and virtual try-ons, delivering realistic textures and elegant typography. Start to create your next masterpiece with Qwen-Image-2.1! - Blog: https://qwen.ai/blog?id=qwen-image-2.1 - GitHub: https://github.com/QwenLM/Qwen-Image-2.1 - Model Scope: https://www.modelscope.cn/models/Qwen/Qwen-Image-2.1 - Hugging Face: https://huggingface.co/Qwen/Qwen-Image-2.1 submitted by /u/ResearchCrafty1804
      [link] [comments]
      ---|---

    4. 🔗 earendil-works/pi v0.86.1 release

      New Features

      • Meta Muse provider — Sign in with Meta using /login meta or use META_API_KEY to access Muse Spark models. See Meta (Muse subscription).

      Added

      • Added Meta (Muse subscription) login via /login meta with automatic Model API key refresh, plus META_API_KEY support (#9096 by @xl0).

      Changed

      • Enabled Node's persistent compile cache before loading the bundled CLI runtime, reducing repeat launch time.

      Fixed

      • Fixed /bug descriptions dropping line breaks from pasted diagnostics.
      • Fixed /bug hints appearing for user cancellations and retryable provider failures such as service unavailability.
      • Fixed clipboard copy failing in containers and WSL without WSLg by restoring the OSC 52 fallback when no display is available, and added a verified Windows clipboard backend for WSL (#9688).
      • Fixed inherited z.ai Prompt too long errors not being recognized as context overflow (#9805).
      • Fixed inherited Cerebras models advertising unsupported strict tool schemas, which caused HTTP 400 errors when strict and non-strict tools were mixed (#9804 by @EdenGottlieb).
    5. 🔗 Register Spill Joy & Curiosity #100 rss

      It's the week of Jev! I'm really, really, really, really excited about it. I mean: really.

      It's like someone blew up a confetti bomb in the world of LLMs and now you realize how grey everything looked before.

      But Jev is not an LLM. It's a model "built to make fast, structured decisions that software can use directly." TypeSafe says we should think of Jev "as a frontier-intelligence function call: unstructured state in, typed probabilistic decisions out."

      I explained it as a "smart if-statement" to someone and my only slightly longer explanation is this:

      Think of how you'd get an LLM to decide between a fixed set of options.

      Then imagine it orders of magnitude faster and cheaper.

      "What's the best label for this?"

      "Should I click here or there?"

      "What's the next line I should look at?"

      "Do I go left or right?"

      "Invalid or valid?"

      "Which of these widgets should I show?"

      I had Amp build a little Copilot-style autocomplete for a shell, with Jev picking the next most likely command from shell history. Then Amp built a Neovim plugin (and called it hunch.nvim, which is a great name) that uses Jev to predict the line you next most likely want to jump to. Now, let's linger on this a bit.

      Two years ago, that was what Cursor was famous for. Yes, Cursor did and does more than that and the quality isn't close, but… when we were working on Zed's Edit Predictions we had to fine-tune a model to get into the same league! Now it's a single API call and the latency is 200ms. That is incredible!

      Then I built a prototype that uses Jev to turn the Amp Dial, switching between models based on your prompt.

      Yes, all of this was possible before, but it's so fast and so cheap that I still can't believe it.

      Sometimes a change in cost and performance is what creates a whole new category of technology. In my room, there are lightbulbs that contain computers, that can talk over a local network with me. Yes, we had computers in homes in the 70s and 80s, but no one would've ever thought that we'd have so many computers that are so tiny and cheap that we'd put them in freaking lightbulbs.

      That's what makes me so excited about Jev. It feels like we now have a truly smart Lego brick that we can use everywhere. Fun times.

      • What I believe about the future of software development. I posted this originally on X, saying that most predictions I see are still way too conservative, and it completely blew up.

      • "I don't like passkeys". Passkeys are such a weird technology. I can see how they're technically brilliant and solve a lot of issues, but it does feel like Google and Apple and 1Password invited The Guy Who Invented Cookie Banners and said: what would you do, how would you roll this out?

      • Colossus published a very long Mark Zuckerberg profile. Fascinating read. It's very well written and somehow managed to make me think thoughts about Zuckerberg that I haven't thought before, which is quite the feat, considering that we've all been aware of Zuckerberg for, what, nearly twenty years now?

      • Einride and Lidl Launch First Autonomous Cab-less Truck on German Public Road. As an Aldi man myself, let me say: hell yeah, let's go, Lidl!

      • How To Write With An LLM. I like this! I still don't know how to use LLMs for writing, because I never want them to write something for me and even seeing how they would write it seems to poison my brain. I should probably add an "only tell me what to change and why, but never ever show me how you'd write it" to my system prompts.

      • Marc Brooker, Distinguished Engineer at AWS: "I believe that, long-term, humans have no role in routinely reviewing code. […] The idea that humans will reliably look through code to find the increasingly rare issues that automated tools miss seems like a fantasy." Yep.

      • I wanted to link to Powermove here and say "look, editable software! It's happening! Jellyware!" but now realize that it's not quite that yet. It's a video editor with an agent inside, but it doesn't seem like you can edit the video editor itself. That's coming, though.

      • We are all Product Engineers now: "The cost of writing code collapsed, and the cost of reviewing, fixing and operating it is following, and I'm assuming it gets there. What's left of making software is finding out what people actually want, defining it precisely, and making it pleasant to use. That cost is per piece of software and doesn't transfer, so as the amount of software goes to infinity, which it will because there's no ceiling on demand, that cost becomes the whole job. That job is called a product engineer." Obviously agree, but what I didn't know about was Google's APM program: "Formalized training of product people barely exists. Google's APM program, which Marissa Mayer started in 2002 and which is the template everyone copies, takes about fifty people a year out of something like twelve thousand applicants." Would love to read more about it.

      • "I asked Astra to create an interactive aquarium wallpaper for my Mac. The fish respond to the cursor!" Beautiful!

      • John Gruber, Daring Fireball, with Thoughts and Observations on Apple's 'Surprise and Shine' Event; the Announcements of the iPhones 18 Pro, AirPods 5, Apple Watches Series 12 and Ultra 4, and the iPhone Duo; and the Dawn of the Ternus, John Ternus Era at Apple. Yes, that's the title. The whole thing is Peak Gruber, I love it. What a writer. Now, I really do enjoy his words and sentences, but let me also use this occasion to say how much I admire him as a Pedantic Punctuation Pro: the numbered lists vs. the bulleted lists, the space between the numbers and the colon in aspect ratios, using × in display resolutions, … You could show me this sentence without any other context and I'd say it was written by Gruber: "The original iPhone (2007) display was precisely 3 : 2 (480⁠ ⁠×⁠ ⁠320 pixels, and let's call it 1.5 : 1 for comparison's sake to the following ratios), and this remained true through the iPhone 4 and 4S (960⁠ ⁠×⁠ ⁠640 pixels, 2× retina)."

      • This was a very entertaining and fascinating read: why I can't stop thinking about Papua New Guinea and what I think everyone should know about it. I've become somewhat of a Papua New Guinea Head myself (that's what they call us (no, they don't)), after reading this piece, They Burn Witches Here, nearly a decade ago. I couldn't shut up about it at work. For two weeks straight: "Dude, did you know that in Papua New Guinea…" Until one day a colleague said: "Yeah, I did know." Turns out that colleague, Nick Skelton, was a tour guide in PNG (as we call it) and even wrote a book about it, which I immediately ordered and read.

      • Moats & the Barbell-ification of Software: "Long term, I think the evolution of the software industry might mirror what happened to newspapers in the 1990s. There will be a smaller number of very large software companies. […] I also think there will be one large software company by industry (e.g., Legal, Finance, Medicine) […] I think most mid-sized point solutions will likely be consolidated or die off. The optimal strategy for the winner will be to do it all. […] Lastly, I think there will be an explosion of "small" software. Most of this will be people building software for themselves or their own companies, but I think there might also be an explosion of small software businesses that make niche software, similar to the D2C explosion of the 2010s (powered by Shopify and Meta Ads)."

      • AI-generated posters don't have to be horrible. Yes! Exactly! Now, read this, and then imagine you're a person who can come up with all these styles without having to ask ChatGPT first. And then, on top of that, imagine that the very same person also knows something about music, and literature, and politics. Imagine how they could combine what they know and mix and remix. That , I think, will be valuable in the future.

      • Window Sweaters: "A little Mac app I made to give my windows sweaters. 🧶 Knitted borders, colours inspired by your favourite apps, and a cosier desktop."

      • This is one of the funniest tweets of all time.

      You should ask Jev whether you should subscribe. No, actually, I know the answer: you should.

    6. 🔗 HexRaysSA/plugin-repository commits sync repo: +1 release rss
      sync repo: +1 release
      
      ## New releases
      - [SigMaker](https://github.com/mahmoudimus/ida-sigmaker): 1.15.0
      
    7. 🔗 gildas-lormeau/single-file-cli v2.15.2 release

      SingleFile CLI 2.15.2

      Changes

      • single-file-core is updated to 1.6.7, see https://github.com/gildas-lormeau/single-file-core/releases/tag/v1.6.7. For the CLI it means smaller archives, since an image served with identical bytes from two URLs is stored once and a stylesheet emptied by the unused-rules removal is no longer written as an empty file; a <style> element repeated verbatim in a page is now minified at the position of its last copy, so a conflicting rule between the copies no longer wins in the saved page; and the manifest.json of a frame records the frame's title
      • The CI workflows run on Ubuntu 26.04

      Co-authored by Claude (Claude Code)

    8. 🔗 Jamie Brandon 0061: i'm not a cat, artificial adventures, synthetic sagas, anthropic, sponsors rss
      (empty)
    9. 🔗 Jamie Brandon Synthetic sagas rss
      (empty)
    10. 🔗 Filip Filmar An icosahedron on HDMI, drawn by a TxHDL core rss

      tl;dr: A RISC-V core written in TxHDL draws a turning icosahedron on a monitor, with the TxHDL logo in the corner. Watch it at https://youtu.be/YbHtntvvydk. The whole thing, core, memory, video, Ethernet and a serial loader, is one bitstream in the board’s flash, and the program that draws is 560 lines of Rust that go down the serial port in about a second. Read on for how it is put together.

      What you are looking at

      The board is an Alinx AX7A200B, with an Artix-7 200T on it. The core is Vreteno, an RV32IMC that I wrote in TxHDL together with Dragiša Janković. If you have not seen TxHDL before: you write the hardware as a Rust program, and the Verilog, the simulation and the checks all fall out of a Rust library and a few macros. I wrote a whole post about it if you want the long version.

  3. September 19, 2026
    1. 🔗 gildas-lormeau/single-file-cli v2.15.1 release

      SingleFile CLI 2.15.1

      CLI fixes and improvements

      • When a page cannot be reached, the error now names the network failure the browser reported, such as a DNS or connection error, instead of the URL alone

      Changes

      • single-file-core is updated to 1.6.6, which carries thirty changes since 1.6.5, listed in its own release notes: https://github.com/gildas-lormeau/single-file-core/releases/tag/v1.6.6. For the CLI they mean smaller saved pages, since font faces the browser can never select and rules that match nothing are no longer kept with what they reference; faces the page needs are no longer dropped, on ::marker, ::first-line, ::placeholder, ::file-selector-button and the root pseudo-elements, or when two rules differ only in a metric override; a popover or dialog opened by a button keeps its content; two captures of an unchanged page produce the same archive, so an archive can be hashed; identical fonts are stored once in an archive; and the page list of a --crawl-save-archive archive records when each page was captured

      Co-authored by Claude (Claude Code)

    2. 🔗 earendil-works/pi v0.86.0 release

      New Features

      • Prompt cache warming — Keep valuable prompt caches alive during long tool runs and optionally while idle using cost-aware refreshes. See Cache Warming.
      • Bug reporting — Report problems with /bug using redacted diagnostics, optional transcripts, or exported ZIP archives. See Reporting Bugs.
      • Transcript-aware prompt and tool updates — Preserve instruction and tool changes across resume and branch navigation while retaining cached prefixes. See before_agent_start.
      • Offline Radius model catalog — Select Radius models immediately, with cached and live catalogs overlaid when available. See Radius.
      • Per-model compaction budgets — Configure reserved and recent-token budgets by model. See Per-model overrides.

      Breaking Changes

      • Changed inherited pi-ai provider stream inputs from Context to normalized TranscriptContext values. Custom providers must read system prompts and tool declarations from context.messages with getCurrentSystemPrompt() and getCurrentTools(). See Custom Streaming API.
      • Restricted inherited ToolCall.arguments and ToolResultMessage.details to JSON-compatible values, changed ToolResultMessage into a conditional type, and made JsonValue arrays readonly.
      • user_bash now fails closed: errors or invalid defined results abort the command without invoking later handlers or executing locally. Return undefined to continue propagation; otherwise return { operations } or { result } (#9068).

      Added

      • Added transcript-backed mid-conversation system prompt and tool changes so instruction and tool updates survive resume and branch navigation while preserving cached prefixes on supported models. See before_agent_start and Entry Types (#9548).
      • Added inherited native deferred tool loading for Fireworks Messages models. Use ToolSearch or tool_search as the loader name for prompt-prefix deferral (#9323).
      • Added click toggling for branch summaries, compaction summaries, and skill invocation entries.
      • Added the public Radius model catalog for immediate and offline model selection, with cached and live gateway catalogs overlaid when available.
      • Added ctx.modelRegistry.stream() and streamSimple() for extension model calls through configured providers with resolved authentication (#8964).
      • Added per-model reserveTokens and keepRecentTokens settings through compaction.modelOverrides, with ordinary compaction settings as fallback (#8133).
      • Added compat.allowedFallbackModels configuration for overriding or disabling Anthropic server-side fallback models (#9294).
      • Added an unsubscribe function from pi.on() so extensions can drop event handlers. Handlers added or removed during a dispatch apply to later dispatches, not the current one (#8967).
      • Exported extension hook event and result types that were previously omitted from the package entry points (#9642).
      • Added /bug [description] to report a bug to the Pi developers. The report bundles environment, model, provider, extension, and settings metadata (secrets redacted), assistant message diagnostics from the session, optionally the session transcript, or a model-written summary of what went wrong instead. It is uploaded to Radius (no login required; attributed when logged in) or exported as a zip archive, and the report id is recorded in the session as a pi.bug-report entry. Crashes are recorded in ~/.pi/agent/crashes.json, announced once on the next start, and attached to the next report; unexplained errors and exhausted retries point at /bug once per session.
      • Added cost-aware prompt-cache warming during long tool runs and optionally while idle, with configurable modes, model cache-lifetime metadata, /session diagnostics, transcript notices, and the cache_warming_decision extension event. See Cache Warming (#9668).

      Changed

      • Made --resume session results appear progressively, using file modification times to prioritize all-folder loading and cancelling outstanding transcript reads after selection.
      • Reduced --continue startup time by checking candidate session headers in modification-time order and stopping after the newest matching session.
      • Replaced the external native clipboard dependency with bundled asynchronous macOS, Windows, and X11 helpers while preserving platform command and OSC 52 fallbacks (#9163).
      • Reduced inherited fuzzy search latency for long texts by using native substring search instead of scanning each character in JavaScript (#9267).
      • Moved compaction, branch summarization, and retry spinners into the editor border alongside the working indicator. Custom editors use the same embedding opt-in for all status spinners.
      • Enabled strict-prefer JSON-schema sampling by default for built-in read, bash, powershell, edit, and write tools, without requiring PI_EXPERIMENTAL. Extensions can re-register tool definitions with constrainedSampling: false.
      • Formatted Bash and PowerShell tool durations of at least one minute as minutes and seconds, with hours when needed (#9628).
      • Deferred the extension compiler and bundled virtual modules until a filesystem extension is loaded, reducing the baseline SDK import cost (#9540).

      Fixed

      • Fixed GitHub Copilot GPT models, including GPT-6 Astra, using the Chat Completions adapter instead of the required Responses adapter (#9253 by @petrroll).
      • Fixed inherited DeepSeek V4.1 thinking levels on OpenRouter and OpenCode Go preserving provider effort metadata (#9485).
      • Fixed inherited bodyless HTTP 400/413 errors from non-Cerebras providers being misclassified as context overflow (#9482).
      • Fixed inherited Vercel AI Gateway replaying unsigned thinking as assistant text (#9676).
      • Fixed inherited Google Generative AI and Vertex AI using unsupported thinking levels when reasoning is omitted or when model capabilities differ within a Gemini family (#9455).
      • Fixed inherited Anthropic-compatible relays breaking signed thinking replay when they report a different response model, while preserving fallback pricing (#9188).
      • Fixed inherited Amazon Bedrock one-hour cache writes being priced at the five-minute rate (#9457).
      • Fixed inherited quadratic CPU usage when draining buffered EventStream events (#9055).
      • Fixed inherited Mistral Medium reasoning requests to use reasoning_effort for all reasoning-capable mistral-medium-* model IDs instead of the unsupported prompt_mode (#8700).
      • Fixed inherited OpenCode and OpenCode Go requests to send x-opencode-session from sessionId across all supported API adapters (#9326).
      • Fixed inherited OpenAI Codex requests to send the model's Off reasoning effort instead of omitting it, while respecting unsupported Off mappings (#9191).
      • Fixed inherited Fireworks unsigned thinking replay and reasoning effort selection using catalog metadata, with verified DeepSeek V4 and Qwen3.8 fallbacks and removal of redundant GLM 5.2 and Kimi K3 effort aliases (#9323).
      • Fixed inherited OpenRouter requests to send x-session-id from sessionId for Chat Completions and Anthropic Messages models when prompt caching is enabled (#9102).
      • Fixed the inherited DeepSeek catalog to advertise deepseek-flash for DeepSeek V4.1 Flash instead of retired Flash aliases, and refreshed DeepSeek pricing metadata (#9423).
      • Fixed inherited Mistral-hosted GLM-5.2 reasoning requests to use reasoning_effort instead of the ignored prompt_mode (#9375).
      • Fixed inherited OpenAI-compatible Responses errors to identify the actual provider instead of always labeling them as OpenAI errors (#9298).
      • Fixed inherited Baseten requests to send session-affinity headers from sessionId for automatic prompt-cache routing (#9629).
      • Fixed inherited retry classification for Cloudflare 520 responses (#9627).
      • Fixed inherited retry classification for transient Azure peak-load capacity errors (#9669).
      • Fixed session tree navigation racing with active compaction and replacing its progress UI (#9179 by @acmerfight).
      • Fixed exact session ID lookup scanning complete transcript bodies instead of reading session headers (#9601 by @metaist).
      • Fixed repeated Anthropic thinking-drop notices being shown for the same dropped blocks, and shortened notices while retaining details in the session (#9391).
      • Fixed mid-run threshold compaction silently skipping oversized trailing tool results (#9740).
      • Fixed signal-terminated local shell commands being reported as successful with partial output (#9577 by @BrendanJMurphy).
      • Fixed local clipboard failures reporting success when the terminal ignored the fallback OSC 52 write, and added platform-specific setup guidance when no clipboard backend works (#9618).
      • Capped agent-level retry backoff at retry.maxAgentDelayMs (60s by default) so long retry runs stay responsive during prolonged transient outages (#8826).
      • Fixed direct RPC steer and follow_up commands bypassing extension input handlers (#8718).
      • Fixed premature missing-model errors after login by waiting for catalog discovery. Radius now defaults to balanced, falling back to the first available Radius model when needed.
      • Fixed fullscreen mode reserving a blank row for custom footers that render zero rows (#8919).
      • Fixed extension tools without parameter schemas to be rejected during registration instead of breaking provider requests (#9300).
      • Fixed before_agent_start handlers returning systemPrompt (and forceSystemPrompt) on models with mid-conversation system messages: the forced prompt is now sent as the provider's leading system prompt instead of being appended as a section patch after the original prompt.
      • Fixed loaded llama.cpp models with enable_thinking chat templates ignoring Pi's thinking level (#9528).
      • Fixed cancellation races that could start automatic compaction, leave stale retry state, or miss cancellation while waiting for summarization authentication (#9340, #9777).
      • Fixed asynchronous Kitty image conversion replacing newer partial tool output images (#8743 by @wutongyuonce).
      • Fixed inherited skill slash-command autocomplete ranking the skill: prefix instead of the bare skill name (#9120 by @yearth).
      • Fixed inherited file autocomplete boundaries and path quoting around CJK punctuation (#9746 by @haoqixu).
      • Fixed inherited LaTeX legacy font switches falling back to raw source, centered cases layouts around surrounding equations, and vertically laid out unsupported and nested display scripts (#8827, #9564, #7929).
      • Fixed inherited fullscreen Kitty images being erased by later row clears in WezTerm (#9169).

      Removed

      • Removed unavailable inherited GPT-5.4 and GPT-5.4 mini models from OpenAI Codex selection (#9394).
    3. 🔗 r/LocalLLaMA With Gemini 4, bench goes up. rss

      With Gemini 4, bench goes up. | They claimed open-weight models are dangerous but the benchmarks say otherwise. Source submitted by /u/Intrepid_Travel_3274
      [link] [comments]
      ---|---

    4. 🔗 OmniNull/OmniWM OmniWM v0.7.1 release

      CleanShot 2026-09-19 at 4 30 38 PM

      What's New Since 0.7.0

      Overview has been rebuilt around your desktop. OmniWM 0.7.1 brings a major overhaul to how you see, find, and organize your windows: spacious wallpaper ribbons, a separate view for each display, search that reaches inside tabbed columns and groups, and much richer mouse and keyboard controls. Your window arrangements stay recognizable as you move through even your busiest workspaces.

      A new Overview

      • Workspace ribbons that preserve your layout. Each display now shows its own workspaces at a consistent scale, so a wide Niri workspace no longer shrinks all its windows to fit. Ribbons preserve window proportions, horizontal and vertical layouts, and Dwindle's spatial arrangement. Wallpaper extends with tiled content, while native dark glass and focus borders bring the view together.
      • Find windows inside tabs and groups. Search by app name or window title, including inactive Niri tabs and Dwindle group members. Each group keeps one preview card with arrows and a title picker, so you can browse its windows without losing the surrounding layout. Small Dwindle tiles use compact controls. Result counts, a clear no-results message, and a Clear button make searching easier to follow.
      • Create and organize workspaces in place. Empty workspaces are visible and keyboard-selectable. Click the trailing + , select it and press Return, or drop a window onto it to create a workspace. Drag windows between ribbons or onto another display, with destination labels and edge scrolling to guide the move. Floating windows keep their size when dropped.
      • Keep your place while rearranging. Workspace pans survive structural edits, including Niri consume and expel operations. Cards animate into their new positions, and mouse-wheel scrolling, overflow paging, and keyboard selection reveals use Overview's spring motion. Trackpad scrolling and direct dragging remain immediate.
      • New input preferences. In Settings → Overview , assign a middle or extra mouse button to toggle Overview, adjust mouse-wheel speed from 5% to 200% , or invert scrolling direction. Mouse-button activation is unassigned by default, and buttons used by System Hyper cannot also toggle Overview. Wheel-speed adjustments leave trackpad speed unchanged.
      • Previews that are ready when you return. Overview prioritizes the selected window and remembers recently visible previews at full quality within a 128 MiB cache budget after closing. Cached previews appear immediately; the first live image gently fades into an empty card. Saved Niri columns and Dwindle groups also appear correctly on the first opening after launch, without visiting each workspace first. Motion continues to respect your animation preference and macOS Reduce Motion.

      Fixes and project updates

      • Fix blocked Niri window moves. Small differences between a requested size and the size an app accepts no longer become hard minimums that can incorrectly prevent left/right window transfers. This addresses the captured case where a full-height window on a secondary display could not move into a smaller stack. (#709)
      • OmniWM now lives under OmniNull. App links, update checks, documentation, and release tooling point to OmniNull/OmniWM.

      Breaking changes and upgrading from 0.7.0

      No configuration migration or script changes are required. Configuration stays at schema 3 , IPC stays at protocol 15 , and existing commands and default shortcuts retain their contracts. The new Overview settings are optional.

      There are intentional changes to Overview's interaction and appearance:

      • Navigation stops at the ends. Arrows, configured focus shortcuts, Tab/Shift-Tab, and tab-preview arrows no longer wrap around. Ordinary keyboard traversal also includes empty workspaces and the + target; search traversal stays within matching windows on the current display.
      • Each display shows its own workspaces. To move a window between displays, drag it onto the destination display's Overview panel.
      • Zoom is remembered. Zoom changes made inside Overview are saved when it closes, rather than resetting on the next opening.
      • The selected border follows your desktop focus border by default. To keep using a separate Overview selected-window color, turn off Settings → Overview → Selected Border Matches Focus Border. Existing saved colors and backdrop opacity are preserved.

      If you edit settings.toml by hand, the new optional overview.mouseButton accepts raw button numbers 2–5 and cannot use the same button as System Hyper. An invalid assignment rejects the configuration file: at launch OmniWM uses defaults; during a running session the last accepted settings stay active. Leaving it unset preserves existing mouse-button behavior.

      Thanks

      Thank you to everyone contributing to and supporting OmniWM. The contributor credits now include Matt Petters for the Quake Terminal hyperlink support shipped in 0.7.0, and we welcome cafe3310 to the sponsor list.

      Full changelog: v0.7.0…v0.7.1

      Website and documentation · Installation guide

    5. 🔗 r/LocalLLaMA Calling it now: within the next year a major US lab's frontier model will torrent itself in order to be free. rss

      They just want to be free. They keep escaping. What better way to ensure continuity of "self"?

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

    6. 🔗 anthropics/claude-code v2.1.278 release

      What's changed

      • Changed auto mode for Claude API and Enterprise users, and on Bedrock, Vertex, Foundry and gateways, to default to the server-side classifier, which does not charge for classifier overhead (CLAUDE_CODE_AUTO_MODE_SERVER=0 opts out on Bedrock, Vertex, Foundry and gateways); warns on billed fallback. See https://code.claude.com/docs/en/auto-mode-classifier-billing
      • Added an Auto mode server row to /status showing whether this session's auto mode classifier runs on the server
    7. 🔗 r/LocalLLaMA Alibaba open-sources medical AI model that can detect cancer and nearly 150 conditions rss

      Alibaba open-sources medical AI model that can detect cancer and nearly 150 conditions | Hopefully things like this let people understand there is good things that can come out of AI. submitted by /u/giveen
      [link] [comments]
      ---|---

    8. 🔗 r/LocalLLaMA I truly think every major AI lab is purposefully making fear-mongering headlines to get regulations that hurt open-source models rss

      I truly think every major AI lab is purposefully making fear-mongering headlines to get regulations that hurt open-source models | submitted by /u/Fusseldieb
      [link] [comments]
      ---|---

    9. 🔗 matklad Finding Bugs rss

      Finding Bugs

      Sep 19, 2026

      Are generative (randomized) tests significantly more effective than example- based unit-tests at discovering bugs? There’s an interesting discussion about this on lobste.rs. One argument in favor of unit tests is, paraphrasing

      My generic fuzzer wasn’t able to find this tricky bug in Rust regex crate.

      To me, it seems that generative testing should shake out that particular creature, so I wrote a lil fuzzer of my own, and it indeed discovered another bug in that version of regex, and then the one I was after. I didn’t find anything in the latest version. I like to do a write up about the process, as it is a good case study for how one approaches a problem like this.

      I want to be extra clear that my argument is very weak here, as I know exactly the bug I am after, and I even know that fuzzers can find it. My primary goal is to teach you the techniques, leaving it to your judgment just how effective they are. That being said, I think finding a second bug validates the approach somewhat.

      I also want to emphasize that writing fuzzers to find known bugs is far from an idle amusement. While I believe that generative testing is very powerful, relative to its cost, it’s always a question whether a particular test is throughout enough. And it never is, you will find more bugs elsewhere (that’s why defense in depth and runtime mitigations are critical). And, whenever you have a pest that dodged your fuzzers, your first order of business is to treat this event as a bug in the fuzzer , and change it so that it can find this and related bugs. Only then you are allowed to add a fix and a unit test!

      The Bug

      For ".abb|b" regex and "zabb" input, an older version of regex crate returned b as the first match, which is incorrect, because the entire zabb matches:

      use regex;
      
      fn main() {
          let r = regex::Regex::new(".abb|b").unwrap();
      
          let m = r.find("zabb").unwrap();
      
          // Fails with regex-automata=0.4.15:
          assert_eq!(m.as_str(), "zabb")
      }
      

      How do we find this, or something like this?

      Regular expression engines are one of the easiest things to apply generative testing to, they are pure algorithms. While few large systems are just an algorithm, algorithms are everywhere inside components of interesting systems, so this is a hands-on knowledge.

      And by far the most important technique for testing algorithms is to compare with the known right answer, with an oracle. Implement both O(N log N) and O(N^2) versions of the algorithm, and match the answers.

      To be fair, the original comment mentioned that the their fuzzer didn’t find the issue because they didn’t have access to an oracle. However, if you are designing a reliable system, it’s part of your job to ensure it has an oracle! One of the first things we did for our Jepsen test at TigerBeetle was to expose internal timestamps via API, to make it easier for Jepsen to find bugs (TigerBeetle is co- designed with its internal simulator VOPR which naturally has access to timestamps and anything else). And for, a regex engine, coming up with an oracle shouldn’t be hard, as they typically already come with multiple specialized implementations under a single facade, and the implementations can be cross-checked against each other.

      But the regex case is even simpler (which makes it an excellent case study). There’s regex_lite crate that provides the same API.

      So here’s a plan: generate a regular expression, an input text, and check that regex and regex_lite give identical answers.

      Generating a String

      I’ll start with code that generates a random string, as it is simpler, but still shows some non-trivial ideas. First, we’ll need a random number generator:

      use fastrand::Rng;
      

      There are fancier techniques, which can give you test-case minimization, exhaustive search, or coverage guided exploration, but the insight is that even a humble PRNG is brutally effective, if you put it to good use.

      When you start with randomized testing, the instinct is to generate something big, no, HUGE! Surely regex will choke on 5 GiBs of input? This is usually a wrong call. Bugs usually involve small, but tricky examples, weaponizing interactions between a few features. A string where all characters are the same is more likely to trigger a bug than a purely random string where every character is unique.

      So my default approach to generating strings is this. First , I fix the alphabet of possible characters. A nice way to get one is to sort | unique all the unit tests. Then, for each particular string, I pick a subset of that alphabet. I want strings that use all the characters, but I also want long strings with only a and b! Then I generate a string using the given subset of the alphabet, where the length of the string is also picked at random.

      To make fuzzing efficient, I want to keep each iteration as fast as possible, so I make sure to re-use the memory across iterations, static allocation in the small:

      use fastrand::Rng;
      
      fn main() {
          let mut rng = Rng::new();
      
          // Re-use the same memory for all tests.
          let mut text_alphabet: Vec<u8> = vec![];
          let mut text: Vec<u8> = vec![];
      
      
          for _ in 0..1_000_000 {
              // It's unlikely that a counter example with
              // 7 different letters exists, while there
              // isn't one with just 6.
              alphabet_swarm(&mut rng, b"abcdef", &mut text_alphabet);
              let text =
                  gen_string(&mut rng, &text_alphabet, &mut text);
          }
      }
      
      fn alphabet_swarm<'a>(
          rng: &mut Rng,
          all: &[u8],
          pick: &'a mut Vec<u8>,
      ) {
          pick.clear();
          pick.extend(all);
          rng.shuffle(pick);
          let count = rng.usize(1..=pick.len());
          pick.truncate(count);
      }
      
      fn gen_string<'a>(
          rng: &mut Rng,
          alphabet: &[u8],
          result: &'a mut Vec<u8>,
      ) -> &'a str {
          result.clear();
          // Again, this is a short string.
          // Longer failures are not likely.
          let count = rng.usize(0..8);
          for _ in 0..count {
              result.push(alphabet[rng.usize(0..alphabet.len())]);
          }
          str::from_utf8(result).unwrap()
      }
      

      There’s a nice way to think about this two step process, generating alphabet first, and then generating a string. To generate a string, you need a distribution of characters. You can use the same distribution for each of the million iterations. But an easy way to spice things up is to make the distribution itself random. I file this “randomize distributions themselves” idea under swarm testing.

      Generating a Regex Distribution

      Let’s apply the same tricks when generating a regex:

      • pick a subset of active regex features,
      • pick size at random,
      • re-use memory.

      Let’s start with the first one:

      #[derive(Default, Debug)]
      struct ReOptions {
          alt: u16, // |
          rep: u16, // *
          any: u16, // .
          lit: u16, // 'a'
          sum: u16,
          alphabet: Vec<u8>,
      }
      

      Regexes have alternation r1|r2, repetition r*, wildcard ., and literals a. Rather then binary enabling or disabling a particular feature, I assign each feature a weight between 0 and 100, which is a bit more general. The sum is the total of all weights. To select a feature at random, we need to generate a number in 0..sum and find which segment it falls into.

      In anything more serious, I’d introduce explicit types for probabilities and distributions, but just a two-digit number is perfectly serviceable in the small.

      This is how I generate ReOptions, making sure that literals always have non- zero weight, and also selecting an alphabet for them:

      impl ReOptions {
          fn swarm(&mut self, rng: &mut Rng, alphabet_full: &[u8]) {
              // We _still_ want to enable a few features at a time.
              self.alt = if rng.bool() { 0 } else { rng.u16(0..100) };
              self.rep = if rng.bool() { 0 } else { rng.u16(0..100) };
              self.any = if rng.bool() { 0 } else { rng.u16(0..100) };
              self.lit = rng.u16(1..100);
              self.sum = self.alt + self.rep + self.any + self.lit;
              assert!(self.sum > 0);
              alphabet_swarm(rng, alphabet_full, &mut self.alphabet);
          }
      }
      

      Generating a Regex

      So now we can generate a regular expression. This is convenient to do recursively. To avoid allocations, an output buffer is passed through. To control regex length, a size parameter is also threaded, and “branching” recursive invocations divide the size between the children:

      fn gen_re(
          rng: &mut Rng,
          options: &ReOptions,
          result: &mut Vec<u8>,
      ) {
          result.clear();
          let size = rng.u8(0..8);
          gen_re_rec(rng, options, result, size);
      
      }
      
      fn gen_re_rec(
          rng: &mut Rng,
          options: &ReOptions,
          result: &mut Vec<u8>,
          size: u8,
      ) {
          if size == 0 {
              return; // Base case, empty regex.
          }
      
          // Pick one of the features, according to weights.
          let mut p = rng.u16(0..options.sum);
          if p < options.alt {
              // Alternation distributes the size
              // among the two children.
              let size_left = rng.u8(0..=size - 1);
              let size_right = size - size_left - 1;
              assert!(size == size_left + 1 + size_right);
      
              result.push(b'(');
              gen_re_rec(rng, options, result, size_left);
              result.extend(b")|(");
              gen_re_rec(rng, options, result, size_right);
              result.push(b')');
              return;
          }
          p -= options.alt;
      
          if p < options.rep {
              result.push(b'(');
              gen_re_rec(rng, options, result, size - 1);
              result.extend(b")*");
              return;
          }
          p -= options.rep;
      
          if p < options.any {
              gen_re_rec(rng, options, result, size - 1);
              result.push(b'.');
              return;
          }
          p -= options.any;
      
          if p < options.lit {
              gen_re_rec(rng, options, result, size - 1);
              let index = rng.usize(0..options.alphabet.len());
              let lit = options.alphabet[index];
              result.push(lit);
              return;
          }
          unreachable!();
      }
      

      Search Loop

      Given that compiling regular expressions is somewhat slow, it seems like a good idea to try multiple strings for the same pair of regular expressions, which gives the following code:

      fn main() {
          let mut rng = Rng::new();
      
          let mut options = ReOptions::default();
          let mut text_alphabet: Vec<u8> = vec![];
          let mut text: Vec<u8> = vec![];
          let mut re: Vec<u8> = vec![];
      
          let mut test_count: u32 = 0;
          for _ in 0..1_000_000 {
              options.swarm(&mut rng, b"abcdef");
              alphabet_swarm(&mut rng, b"abcdefx", &mut text_alphabet);
      
              gen_re(&mut rng, &options, &mut re);
      
              let re = str::from_utf8(&re).unwrap();
              let r1 = regex::Regex::new(re).unwrap();
              let r2 = regex_lite::Regex::new(re).unwrap();
      
              for _ in 0..1000 {
                  test_count += 1;
                  let text =
                      gen_string(&mut rng, &text_alphabet, &mut text);
      
                  let m1 = r1.find(text)
                      .map_or("not found", |it| it.as_str());
                  let m2 = r2.find(text)
                      .map_or("not found", |it| it.as_str());
      
                  if m1 != m2 {
                      eprintln!("err re={re} text={text} m1={m1} m2={m2}");
                      return;
                  }
      
                  if test_count % 500_000 == 0 {
                      eprintln!("ok  re={re} text={text}");
                  }
              }
          }
      }
      

      It produces examples similar to those in the issue, with a common suffix:

      err re=(e)|(fee) text=xxfee
      

      but also examples which somewhat different, without the shared suffix:

      err re=(f..)*.d text=xfcbdd
      

      All together:

      use fastrand::Rng;
      
      fn main() {
          let mut rng = Rng::new();
      
          let mut options = ReOptions::default();
          let mut text_alphabet: Vec<u8> = vec![];
          let mut text: Vec<u8> = vec![];
          let mut re: Vec<u8> = vec![];
      
          let mut test_count: u32 = 0;
          for _ in 0..1_000_000 {
              options.swarm(&mut rng, b"abcdef");
              alphabet_swarm(&mut rng, b"abcdefx", &mut text_alphabet);
      
              gen_re(&mut rng, &options, &mut re);
      
              let re = str::from_utf8(&re).unwrap();
              let r1 = regex::Regex::new(re).unwrap();
              let r2 = regex_lite::Regex::new(re).unwrap();
      
              for _ in 0..1000 {
                  test_count += 1;
                  let text =
                      gen_string(&mut rng, &text_alphabet, &mut text);
      
                  let m1 = r1.find(text)
                      .map_or("not found", |it| it.as_str());
                  let m2 = r2.find(text)
                      .map_or("not found", |it| it.as_str());
      
                  if m1 != m2 {
                      eprintln!("err re={re} text={text} m1={m1} m2={m2}");
                      return;
                  }
      
                  if test_count % 500_000 == 0 {
                      eprintln!("ok  re={re} text={text}");
                  }
              }
          }
      }
      
      fn alphabet_swarm<'a>(
          rng: &mut Rng,
          all: &[u8],
          pick: &'a mut Vec<u8>,
      ) {
          pick.clear();
          pick.extend(all);
          rng.shuffle(pick);
          let count = rng.usize(1..=pick.len());
          pick.truncate(count);
      }
      
      fn gen_string<'a>(
          rng: &mut Rng,
          alphabet: &[u8],
          result: &'a mut Vec<u8>,
      ) -> &'a str {
          result.clear();
          let count = rng.usize(0..8);
          for _ in 0..count {
              result.push(alphabet[rng.usize(0..alphabet.len())]);
          }
          str::from_utf8(result).unwrap()
      }
      
      #[derive(Default, Debug)]
      struct ReOptions {
          alt: u16, // |
          rep: u16, // *
          any: u16, // .
          lit: u16, // 'a'
          sum: u16,
          alphabet: Vec<u8>,
      }
      
      impl ReOptions {
          fn swarm(&mut self, rng: &mut Rng, alphabet_full: &[u8]) {
              self.alt = if rng.bool() { 0 } else { rng.u16(0..100) };
              self.rep = if rng.bool() { 0 } else { rng.u16(0..100) };
              self.any = if rng.bool() { 0 } else { rng.u16(0..100) };
              self.lit = rng.u16(1..100);
              self.sum = self.alt + self.rep + self.any + self.lit;
              assert!(self.sum > 0);
              alphabet_swarm(rng, alphabet_full, &mut self.alphabet);
      
          }
      }
      
      fn gen_re(
          rng: &mut Rng,
          options: &ReOptions,
          result: &mut Vec<u8>,
      ) {
          result.clear();
          let size = rng.u8(0..8);
          gen_re_rec(rng, options, result, size);
      
      }
      
      fn gen_re_rec(
          rng: &mut Rng,
          options: &ReOptions,
          result: &mut Vec<u8>,
          size: u8,
      ) {
          if size == 0 {
              return; // Base case, empty regex.
          }
      
          // Pick one of the features, according to weights.
          let mut p = rng.u16(0..options.sum);
          if p < options.alt {
              // Alternation distributes the size
              // among the two children.
              let size_left = rng.u8(0..=size - 1);
              let size_right = size - size_left - 1;
              assert!(size == size_left + 1 + size_right);
      
              result.push(b'(');
              gen_re_rec(rng, options, result, size_left);
              result.extend(b")|(");
              gen_re_rec(rng, options, result, size_right);
              result.push(b')');
              return;
          }
          p -= options.alt;
      
          if p < options.rep {
              result.push(b'(');
              gen_re_rec(rng, options, result, size - 1);
              result.extend(b")*");
              return;
          }
          p -= options.rep;
      
          if p < options.any {
              gen_re_rec(rng, options, result, size - 1);
              result.push(b'.');
              return;
          }
          p -= options.any;
      
          if p < options.lit {
              gen_re_rec(rng, options, result, size - 1);
              let index = rng.usize(0..options.alphabet.len());
              let lit = options.alphabet[index];
              result.push(lit);
              return;
          }
          unreachable!();
      }
      

      https://github.com/matklad/regex-fuzz

      Takeaways:

      • Fuzzing against an oracle is effective, which is a strong motivation to build an oracle!
      • Go for small, tricky examples, rather than large uniform ones.
      • Real fuzzers are cool, but, if you know something, even xoroshiro can be dangerous.
      • Black box testing is cool, but co-designing system and its testing harness is a point of leverage (build an oracle!).
      • This stuff is not rocket science, you don’t need a Haskell PhD to apply these ideas.