🏡


  1. August 26, 2026
    1. 🔗 WerWolv/ImHex Nightly Builds release

      Nightly

      8e67153 Changelog

      • fix: Don't save process memory provider to projects
      • fix: Make sure providers that can't appear as recent files won't be part of projects either
      • fix: Remove unused language strings
      • patterns: Update pattern language
      • impr: Refactor project system into multiple files
      • fix: Prevent same file from appearing multiple times in project
      • patterns: Update pattern language
      • feat: Add support for LZF decompression to the pattern language (#2865)
      • Fix lang conversion to string
  2. August 25, 2026
    1. 🔗 PrimeIntellect-ai/prime-agent Beta (v0.8.0-beta.549.1.b5ee2f8) release

      Automated beta build from main (b5ee2f81a59510e7225a0db10d65102e91e98803).

    2. 🔗 anthropics/claude-code v2.1.246 release

      What's changed

      • Added a startup warning for Bash allow rules with a wildcard before the subcommand (e.g. Bash(git * main)), since they also match options inserted before the subcommand
      • Added an Auto mode tab to /permissions for viewing and editing auto mode classifier rules
      • Added the turn's completion time to the end-of-turn duration line, e.g. ✻ Sautéed for 23s · done 6:05 PM
      • Fixed fullscreen mode showing a blank transcript after resizing the terminal and jumping to the bottom until the next keypress
      • Fixed a severe transcript slowdown when a diff contained a very long single line (e.g. a base64 string); such lines now render truncated with a marker
      • Fixed erratic fullscreen scrolling when positioned at an earlier message, including jump-to-bottom getting stuck mid-transcript
      • Fixed background sessions failing to open after 45 seconds when Claude Code's starting directory had been deleted, the machine had slept, or the host is slow to start processes
      • Fixed background sessions failing to open with "Couldn't start the background service … EACCES" when another Claude Code process was re-installing the npm package at that moment
      • Fixed markdown rendering being disabled for a whole message when its first 500 characters contained no markdown, and for +/N) lists and setext headings
      • Fixed MCP tool calls interrupted by an incoming message in headless/remote sessions being reported to the model as "completed with no output" instead of an explicit interrupted error
      • Fixed MCP tool arguments being sent as JSON strings when the parameter's schema is empty ({}), instead of their real type
      • Fixed a command interrupted mid-run showing as "Ran 1 shell command" with no sign it was cut
      • Fixed pressing ← or running /background during a dynamic workflow restarting its finished subagents; it now asks first and says how many subagents would restart
      • Fixed opening a just-started session in claude agents while its worker was still booting (common on Windows) stopping it with "was stopped while the respawn was in flight"
      • Fixed claude agents listing a backgrounded named session twice; backgrounding the same conversation again now numbers the new row (e.g. my-session (2))
      • Fixed the background retention sweep removing git worktrees under .claude/worktrees/ that you created yourself when an old background-session record pointed at them
      • Fixed auto mode tool calls being denied as "temporarily unavailable" on very large sessions by scaling the safety-check deadline with prompt size
      • Fixed the plugin cache creating duplicate SHA-named directories for the same plugin
      • Fixed plugin skills whose frontmatter name already includes the <plugin>: prefix showing it doubled in the slash menu (e.g. /plugin:plugin:skill)
      • Fixed claude plugin update failing for an installed plugin given its bare name (only the fully-qualified name worked)
      • Fixed plugin installation failing when plugin.json was saved with a UTF-8 byte-order mark (BOM)
      • Fixed /reload-plugins reporting 0 skills for plugins that define skills under skills/*/SKILL.md
      • Fixed hook error messages showing a literal ${CLAUDE_PLUGIN_ROOT} instead of the resolved plugin path
      • Fixed /rename replacing the theme's prompt border color (including a custom theme's promptBorder) with the default cyan; the border now keeps your theme's color unless you pick one with /color
      • Fixed custom theme diff colors (diffAdded/diffRemoved and their dimmed variants) being ignored in diffs and the /theme preview
      • Fixed a keybindings.json binding with an unknown action name silently deadening that key; it is now skipped so the default binding keeps working, and a warning is logged under --debug
      • Fixed /stats activity heatmap showing each day's activity one cell off (Sunday's count under Monday) in timezones east of UTC
      • Fixed /fork from an already-forked or backgrounded session starting the new session with an empty conversation
      • Fixed prompts beginning with /-- (e.g. Lean doc comments) being rejected as an unknown slash command instead of being sent to Claude
      • Fixed the @ file picker staying open after the typed text stopped matching a real path
      • Fixed the status line's cost and duration resetting to zero after navigating to the agents view and back
      • Fixed fullscreen mode moving keyboard focus onto the control under the pointer when you clicked the terminal window only to bring it back into focus
      • Fixed path completion failing when the completion token or working directory contained a null byte
      • Windows/macOS: Fixed headless sessions not cleaning up stale entries in ~/.claude/sessions left by sessions that exited uncleanly
      • Fixed the UI stopping with a render error on the first tool call when a third-party Anthropic-compatible endpoint (ANTHROPIC_BASE_URL) streams a tool_use block without an id
      • Fixed the Write tool reporting "Out of memory" or freezing for a long time after overwriting a very large existing file, even though the file had been written
      • Fixed claude plugin install <name> exiting silently (or hanging in a terminal) instead of reporting an error when ~/.claude/plugins/known_marketplaces.json is empty or corrupted
      • Fixed resumed sessions failing every turn with a 400 when the saved history contains tool blocks the Anthropic API does not accept (typically written by a third-party API proxy)
      • Fixed curl -fsSL https://claude.ai/install.sh | bash failing with "Raw mode is not supported" for some Team/Enterprise users with server-managed settings
      • Fixed sessions that ended in plan mode resuming outside plan mode in the VS Code extension, and in claude -p --continue/--resume with a permission prompt tool, when no permission mode was set
      • Fixed the Notification hook not firing while the sandbox "Network request outside of sandbox" permission prompt is waiting
      • Fixed Bash permission checks to always require approval for malformed commands with a dangling && or || operator
      • Fixed --strict-mcp-config sessions prompting to approve .mcp.json servers they would never load, which left background sessions waiting at startup
      • Fixed telemetry and metrics requests to Anthropic carrying the API key configured for a third-party gateway (ANTHROPIC_BASE_URL); a credential is now only sent to its own host
      • Fixed a visible API error on the first prompt after idle when apiKeyHelper returns short-lived JWTs: an expired cached token is now refreshed before sending, and 401/403 auth errors retry quietly
      • Fixed memory growing with session length in the fullscreen and Ctrl+O transcript views: each rendered message row no longer retains a full copy of the transcript-wide tool lookups
      • Fixed /ultrareview runs and cloud sessions launched at the same time from one repository (e.g. from several worktrees) sometimes starting with another launch's uncommitted changes
      • Fixed the task progress count (e.g. 3/5) shown for background cloud sessions such as /autofix-pr occasionally missing a task
      • Fixed Remote Control sessions keeping their placeholder name in claude.ai and the Claude app until the second prompt; the auto-generated title now appears after the first prompt
      • Fixed MCP tools marked requiresUserInteraction still offering "Yes, and don't ask again" in their permission prompt; the option wrote an allow rule the tool then ignored
      • Fixed the self-hosted runner ending its live sessions or exiting when a work-poll response is malformed (e.g. an intercepting proxy's HTML page); it now retries the poll
      • Improved /cd: the new directory's project settings, hooks, .mcp.json servers (behind the usual approval prompt), skills, and agents now take effect right after the move instead of on --resume
      • Improved Bash tool latency on bash shells by replaying snapshot functions without a base64 subshell per function
      • Improved subagent results: a subagent that stops at its maxTurns limit now returns its output marked as partial, with a hint to continue it via SendMessage, instead of appearing finished
      • Improved non-interactive sessions (-p, SDK, cloud sessions) to automatically continue a response cut off mid-stream by a server error, connection loss, or stall instead of ending with an error
      • Improved attribution of usage telemetry to your organization for workload identity federation sessions, events sent while apiKeyHelper runs at startup, and after a login token expired while idle
      • Changed /code-review so Claude can also start it on its own on Bedrock, Vertex AI, and Foundry, through the Claude apps gateway, and when telemetry or non-essential traffic is disabled
      • /goal: Changed idle sessions to start at most three check-ins on long-running background work per goal; your next message allows three more
      • Changed claude install and claude update to defer a pending managed-settings consent prompt to the next interactive session instead of prompting mid-command
      • Changed OpenTelemetry plugin events for plugins synced from claude.ai: plugin_id_hash now reflects the plugin's real marketplace, and enabled_via is admin-install for admin-installed plugins
      • Fixed the command sandbox's filesystem configuration not respecting --setting-sources
    3. 🔗 modem-dev/hunk v0.20.0 release

      What's Changed

      Hunk 0.20 makes it easier to install, keep current, and extend—while making everyday reviews faster and more precise.

      Highlights

      • Install Hunk on macOS and Linux with a checksum-verified curl installer, then keep npm, Homebrew, and curl installations current with hunk update, by @benvinegar in #799 and #788.
      • Explore a searchable directory of 12 community extensions covering GitHub reviews, Mercurial, search, CODEOWNERS, commit-by-commit workflows, exports, and more, introduced by @benvinegar in #811, with catalog contributions from @phl28, @gentilfp, and @sadick254 in #822, #826, and #842.
      • Build deeper integrations with exact selected-line context and immutable snapshots of reviewed files and saved notes, by @benvinegar in #779 and #833.
      • Click exact diff lines—including blank lines—and open inline comments without shifting their target code, by @benvinegar in #840 and #809.
      • Navigate and revisit highlighted diffs with less work, while --fast now offloads syntax highlighting for eligible files with 40 or more lines, by @benvinegar in #791, #803, and #810.

      Release notes : https://hunk.dev/changelog/0.20/
      Full Changelog : v0.19.0...v0.20.0

    4. 🔗 @binaryninja@infosec.exchange Sidekick for everyone sounded better than Sidekick for some. Today we launched mastodon

      Sidekick for everyone sounded better than Sidekick for some. Today we launched Sidekick Free! If you have an active Binary Ninja license, you now have Sidekick too. No extra purchase or signup needed. See what it can do: https://sidekick.binary.ninja/free

    5. 🔗 r/Harrogate St George's Dance Club rss

      St George's Dance Club | Does anyone know what is happening with the building that used to house the St George's Dance Club? submitted by /u/Sjmumbo
      [link] [comments]
      ---|---

    6. 🔗 modem-dev/hunk v0.19.1 release

      What's Changed

      Hunk 0.19.1 improves CPU compatibility, npm installation, and diff rendering.

      Highlights

      • Run x64 builds on pre-Haswell processors and conservative VM CPU models by @elucid in #797.
      • Install cleanly from npm with executable platform binaries and without unnecessary Pierre dependencies for CLI-only users by @benvinegar in #844 and #847.
      • Keep reviews fast and visually accurate by caching complex Unicode width measurements and preserving Git moved-line colors on wrapped rows by @benvinegar and @HackAttack in #800 and #812.

      Release notes : https://hunk.dev/changelog/0.19/
      Full Changelog : v0.19.0...v0.19.1

    7. 🔗 anthropics/claude-code v2.1.245 release

      What's changed

      • Fixed a crash on startup on Linux distributions that ship glibc 2.44 (for example Arch Linux, CachyOS and Fedora Rawhide)
    8. 🔗 seanmonstar Micro: Using LLMs to find not-that-rock rss

      Imagine someone tells you to get them a rock. You go find one, bring it back, and they reply ‘no, not that rock’. They didn’t know exactly what they wanted, but they knew yours wasn’t it.

      When writing, I always start by dumping a detailed outline with points and subpoints. And then, ugh, I feel a wall. How do I turn this into prose? I ask an LLM to try, I look at the ablated text, and I hate it. No, definitely not that rock. More like this.

  3. August 24, 2026
    1. 🔗 IDA Plugin Updates IDA Plugin Updates on 2026-08-24 rss

      IDA Plugin Updates on 2026-08-24

      New Releases:

      Activity:

      • earthbreakers
        • e81bcca4: Add configurable server map (ebrk.ini/env/cmdline) and document map list
        • 2747a12b: Point default list server at the.yankees-suck.net, rebuild shim, add …
      • ida-nexus
      • xrefgen
        • ccc9afc9: Make installed IDA plugin find its core
        • bb6081e5: Complete corpus ground-truth manifest
        • 5ddb9120: Make IDA smoke tests runnable from wrappers
        • d54c5f81: Add symbolic IDA corpus validation
        • 1b34a973: Keep graph call resolution when skipping insights
        • 8421eca0: Add reproducible multi-language corpus builder
        • 4bf0c825: Make typed result roles authoritative
        • f5bb252e: Add one-command IDA installer
        • 3e4c981d: Gate full and incremental IDA equivalence
        • b5b9cdfc: Harden IDA compatibility test mocks
        • 3442a338: Add ARM64 negative corpus fixture
        • 1e5aca0d: Use real trampoline and deterministic graph sources
        • d36712d9: Add explicit analyzer result emitters
        • 14484a4a: Complete release validation documentation
        • d55a0618: Align effective analysis defaults
        • 36112728: Add ground truth quality gates
        • 7b706eb1: Make full analysis state deterministic
        • 7e6b74fe: Preserve explicit analysis result semantics
        • 10faf86d: Validate control flow at real callsites
        • 4e4170fa: Align defaults and release configuration
    2. 🔗 anthropics/claude-code v2.1.243 release

      chore: Update CHANGELOG.md and feed.xml

    3. 🔗 r/Harrogate Is this a real road between Markington and Ripley? Can I cycle it on a road bike? rss

      Is this a real road between Markington and Ripley? Can I cycle it on a road bike? | Planning a road bike route. And just verifying my route via Google maps, this particular road/section doesn't seem to exist on Google Maps. I'm wary its total nonsense. I dont live in the area. It connects Hinks Hall Lane in Markington to Birthwaite Lane in Ripley... submitted by /u/FoxGlove_113
      [link] [comments]
      ---|---

    4. 🔗 MetaBrainz Blocked ListenBrainz services/User Agent strings rss

      Unfortunately some legitimate services got caught up in our recent attempts to mitigate (yet another) bot-net attack on ListenBrainz.

      You may find that a service you use is now being blocked, including Navidrome. We have also had reports of multi- scrobbler submissions being blocked, and there may be other affected services that haven't yet been reported to us.

      These services got caught up in recent blocks because they are/were not setting a proper user agent when calling the LB API.

      Navidrome and multi-scrobbler are already pushing fixes!

      If you are experiencing issues on another service, we would like to let you through, but without a user agent (UA) string we can't identify the service's traffic. Please ask your service to add a meaningful UA string (in a format like described in https://musicbrainz.org/doc/MusicBrainz_API/Rate_Limiting) so we can get you back up and running.

      Implementing a good back off when they receive a 429 error will help avoid blocks too. For example, extending the waiting time between resubmissions, if the service repeatedly returns a 429. Note that some of the blocked services had this implemented already, but it can help.

      Developers, you are always welcome to talk to us on the dev channel in ChatBrainz (Matrix/IRC/Discord) if you have any questions.

    5. 🔗 r/Harrogate Sewage smell in Bilton area?? rss

      Absolutely honking around here, anyone know what's going on?

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

    6. 🔗 MetaBrainz GSoC 2026: Playlists Sorting and Organization rss

      Helloo Everyone!
      My name is Yateen, a final year cse student at Indian Institute of Technology Jodhpur. My matrix username is (@yateen) and GitHub. I started open source at the beginning of this year. I was selected as a GSoC contributor and worked on the Playlists Sorting and Organization project. It was a wonderful experience working on a tech stack of React, Databases, APIs, Jest + Testing Library, etc. What was more amazing was to work under the mentorship of Ansh Goyal (Github), Monkey (Github) and Jade (Github) and be a part of the wonderful MetaBrainz team.

      Pre-selection Contributions

      Before GSoC officially started, I contributed to ListenBrainz through several smaller PRs that helped me learn the codebase and review process. Firstly I started with exploring some small UI/UX issues such as preventing duplicate error toasts and fixing toast overlap with the YouTube player, accessibility improvements like adding aria-labels to icon-only playback buttons. Then I started solving tickets which are feature-oriented like including auto-save for settings, showing artist name relationships. This pre-selection period helped me get comfortable with ListenBrainz patterns and mentor feedback before taking on the larger GSoC features.

      Project Overview

      Over the summer I worked on the playlist features and improved the existing playlist structure. I shipped four related features that turn ListenBrainz playlists from a flat list into something you can search, tag, sort for listening, and import from MusicBrainz collections.

      Feature 1 - Playlist Search

      The first thing I tackled was search on the user playlists page. If you had a lot of playlists, finding one usually meant scrolling forever or jumping over to global search.

      I started with a simple search bar wired to the existing user playlist search API: type at least three characters, hit search and get matching playlists back with pagination. But there was a problem with it especially once you mixed search with sorting and paging.

      Ansh suggested to treat search like the rest of the playlists pag: put the query in the URL query parameters and load data through the route loader . The main issue now was the search results were automatically sorted before getting displayed with the existing sort option (Date Created, Date Modified, Title, etc.) which doesn't provide a good search experience. So I introduced a way for search results to default to “Best Match ” while a search is active and later you can sort by date or title and get a consistent order across pages. I also added small but important pieces - loading state while rendering and empty messages when "No playlist found".

      Feature 2-Tagging Playlists

      Search helps when you remember a name. Tags help when you remember a use /category : gym, road trip, focus, whatever. ListenBrainz didn’t have real playlist tags yet. Playlists already had additional_metadata, but we needed a proper structure to organize them with tags.

      On the backend , I added a playlist_tag table in admin/timescale/create_tables.sql, with normalization (lowercase, trim whitespace, length limits). Each tag ≤ 40 chars, you can add up to 25 tags per API call; a playlist can hold at most 50 tags overall, and only the playlist owner can add/remove. Collaborators can see tags but can’t edit them.

      Tags show up in playlist JSPF when we need them for the UI, and there’s a sidebar endpoint that returns each tag with how many playlists use it, separately for owned vs collaborative playlists. Filtering supports multiple tags, and it works together with search.

      Table

      Table: playlist.playlist_tag

      Column| Type| Notes
      ---|---|---
      id| SERIAL| Primary key
      playlist_id| INT NOT NULL| FK to playlist.playlist(id), ON DELETE CASCADE
      tag| TEXT NOT NULL| The tag string
      created| TIMESTAMPTZ| Defaults to NOW()

      UNIQUE (playlist_id, tag) — same tag only once per playlist

      On the frontend , you can add tags when creating or editing a playlist, see them on playlist cards and on the playlist page, and filter from a sidebar. Active tags filters are included in the URL (?tag=…), so they combine cleanly with the search and sort features.

      Designing the UI for the tags sidebar was a bit challenging. I consulted with Aerozol, Ansh and Monkey and finally came up with a UI which reuses the existing components from Listenbrainz.

      BEFORE :

      Main playlist page without tags sorti ng

      AFTER:

      Playlists sorted by tags

      Feature 3 - Sorting tracks inside a playlist

      After search and tags, the next issue was browsing tracks inside a playlist. Tracks always showed in the saved order. That is fine for editing, but not always for listening. Sometimes you want A–Z, by artist, newest first, or shuffle, without changing the playlist forever.

      So I built this only on the frontend. On the playlist page, there is a “Sort by ” menu next to Play all. Options are Default, Recently added, Title, Artist, and Shuffle. Default is the real saved order. Drag and drop works only in Default, while other sorts are temporary.

      For Shuffle, if you add or remove a track, the list does not reshuffle again. Play all also uses the order you see on screen. This was a smaller feature than search or tags, but it made long playlists easier to use.

      **Feature 4 - Importing Musicbrainz collections **

      ListenBrainz already imported playlists from Spotify, Apple Music, and SoundCloud, but not from MB itself. The goal was to preview MusicBrainz collections in ListenBrainz and optionally save them as normal ListenBrainz playlists .

      Recording collections first

      We started with recording collections. Instead of calling the MusicBrainz web API, we read live from the MusicBrainz database using MB_DATABASE_URI.

      On the backend:

      • GET /1/playlist/import/musicbrainz/collections lists the logged-in user’s collections.
      • /collection/<mbid>/ shows the collection page which follows the same SPA pattern as other ListenBrainz pages . GET returns the HTML shell, and POST returns the JSON data.
      • Public collections can be viewed by anyone. Private collections are only for the owner, checked with their MusicBrainz editor id.

      On the frontend:

      • A MusicBrainz option was added to the import modal. Clicking a collection opens a preview page where tracks load in pages of 100, with virtual scrolling for large collections. The page also supports cover art, Play all, and Save as playlist. Saving uses the existing playlist create API (POST /1/playlist/create).
      • After mentor review, we improved the page to better match ListenBrainz patterns. The first page of data now loads through RouteLoader, more tracks are fetched with useInfiniteQuery instead of a hand-written fetch loop.

      Release Collections

      Now on the preview page, a release collection does not show a flat list of tracks. It shows the releases themselves — title, artist, and cover art when available. Clicking a release opens the ListenBrainz release page at /release/<mbid>/, so the user can explore that album in the normal ListenBrainz UI.

      Saving works a bit differently from recording collections. A release collection is not already a list of recordings, so the backend flattens each release into its tracklist. The frontend requests this with ?flatten=tracks on the collection page. That query walks the MusicBrainz release → medium → track → recording path and returns normal recording rows. Those rows are then sent to the existing playlist create API, so the final result is a normal ListenBrainz playlist that you can play, edit, and manage like any other playlist.

      Testing

      Writing tests was one of the biggest learning curves for me this summer. Before GSoC, I mostly checked features by hand and did not have a strong habit of adding tests. Working on ListenBrainz changed that. Mentors often asked for coverage of edge cases, auth rules, and validation paths, so writing tests became a normal part of finishing a feature.
      For each feature I used a mix of both manual checks with automated tests:

      • Frontend tests in Playlists.test.tsx for playlist search.
      • Integration tests in test_playlist_api.py for tags.
      • Frontend tests in Playlist.test.tsx for in-playlist sorting.
      • Integration tests in test_musicbrainz_collections_import.py for MusicBrainz collections.

      Overall GSoC Experience

      GSoC with MetaBrainz was my first time working on a large open-source project with real users and a real review process. I learned a lot more than just writing features. Mentors pushed me to think about URL state, loaders, error codes, empty states, and tests — things I used to skip or only check by hand.

      I would like to thank Ansh , Monkey , Jade for guiding me throughout the process . I learned professionalism from them and their dedication to their work motivates me to improve my own. I realized that handling such large organization with limited members requires high skills built on strong fundamentals and obviously a problem solving mindset is must . I would also like to thank Aerozol for helping me in UI related issues.

      What feels most rewarding is knowing the work I shipped will remain in the project and will be used by users across the world.

      My PR's around the GSoC period and Proposal can be found here - PRs

      Proposal

      Post GSoC Plan

      I plan to take a short break and then with fresh energy again start contributing to ListenBrainz and other MetaBrainz products. My next steps will likely include polishing the playlists further and solving other tickets so I stay active in the community (I love Monday meetings 🙂). Long term, I hope to keep growing as an open-source contributor and continue improving ListenBrainz.

    7. 🔗 earendil-works/pi v0.84.3 release

      New Features

      • PowerShell tool — Use optional native PowerShell command execution on Windows. See PowerShell Tool.
      • Safer managed updates — Stage, verify, and atomically activate updates for installer-managed installations. See Install and Manage.
      • Model and thinking controls — Select thinking levels with /thinking, search defaults, keep selections session-scoped, and persist them explicitly with Ctrl+S. See Models and Thinking.

      Breaking Changes

      • Renamed the inherited GoogleThinkingLevel type to GoogleApiThinkingLevel and added ResolvedGoogleThinkingLevel for normalized adapter levels.

      Added

      • Added an optional powershell tool for Windows, configurable through defaultTools and the SDK. See PowerShell Tool.
      • Added a /thinking selector and searchable default choices to the model and thinking selectors; Ctrl+S saves the selected model as the global default. See Models and Thinking.
      • Added optional routing session IDs to exported compaction summary helpers so callers can preserve provider routing without enabling prompt cache writes.
      • Added transcript usage notices for compaction and branch summaries when cache miss notices are enabled.
      • Added session_compact_failed extension events so compaction failures and aborts expose their reason, retry state, source, and error message to handlers (#8175).
      • Added inherited provider-neutral toolChoice support to simple stream requests.
      • Added inherited automatic Anthropic server-side refusal fallback for supported first-party models, including returned-model usage pricing (#8017).
      • Added inherited configurable OpenAI-compatible thinking-token budget fields for vLLM, Qwen/SGLang, and llama.cpp servers. See OpenAI Compatibility (#8275 by @bnsd55).
      • Added inherited China-specific ZAI Coding Plan models, including GLM-4.6V vision support and API-equivalent usage cost estimates (#8220).
      • Added inherited deepseek-v4-pro-0813 support to the Qwen Token Plan Individual catalog (#8194).

      Changed

      • Changed experimental installer-managed installations so pi update stages, verifies, and atomically activates the selected release in place. See Install and Manage.
      • Changed inherited built-in xAI models to use the Responses API with encrypted reasoning replay and made Grok 4.6 the default xAI model (#8124 by @Jaaneek).
      • Changed inherited Anthropic, Azure OpenAI, Google, Mistral, and OpenAI adapters to send Pi's default User-Agent unless overridden (#8305).
      • Changed Windows and WSL keybinding defaults to avoid terminal-reserved shortcuts for image paste, model cycling, editor undo, fullscreen transcript navigation and search, and message queueing (#8372).
      • Changed Bun release archives to ship the native clipboard binary only inside the wrapper package, removing a duplicate platform package from each archive.
      • Changed package resource glob expansion to use Node.js's built-in implementation with deterministic visible-path matching, reducing the installed runtime dependency tree.
      • Changed the bundled Node.js runtime to load jiti only when importing an extension and Babel only when uncached source needs transformation, reducing CLI startup time and bundle size.
      • Changed syntax highlighting to initialize only twenty common languages eagerly and defer the remaining grammars until after the initial TUI render, reducing CLI startup time.
      • Changed the Node.js CLI and RPC entrypoints to load a bundled runtime, reducing startup filesystem reads while keeping the public library and legacy module paths on the modular runtime for normal dependency identity.
      • Changed session sharing to render clickable terminal links, display only the canonical Radius artifact URL, and include the current system prompt and active tool definitions in Radius session shares.

      Fixed

      • Fixed failed extension factories leaving event subscriptions, provider registrations, and default flag state active (#8424 by @acmerfight).
      • Fixed models.json typings omitting the documented OpenAI-compatible compat.supportsFinishReason provider and model override (#8487 by @petrroll).
      • Fixed /model and /thinking selections being persisted globally unless explicitly saved with Ctrl+S (#5263).
      • Fixed JSON and RPC toolcall_start events omitting the tool call id and name (#7953 by @christianklotz).
      • Fixed extensions failing to load when the Node.js CLI runs as a single-executable application (#8237).
      • Fixed nested Markdown skills inside .agents/skills/ grouping directories not being discovered.
      • Fixed compaction and branch summarization requests exposing tools to providers.
      • Fixed single-object edit tool inputs failing validation by accepting them as one-edit arrays in both coding-agent and harness edit tools (#7835).
      • Fixed root Markdown files such as README.md and AGENTS.md in skill directories being reported as broken skills unless they declare valid skill frontmatter (#7805).
      • Fixed the default Cerebras model referencing an unavailable Z.AI model.
      • Fixed inherited OpenAI-compatible Chat Completions reasoning replay to preserve and resend assistant-level reasoning_details verbatim and in order (#7994).
      • Fixed inherited Anthropic server-side fallback responses being priced with the requested model instead of the returned fallback model (#8285).
      • Fixed inherited GitHub Copilot login triggering model-policy rate limits by limiting policy updates, retrying model discovery once, and honoring server retry delays (#7850).
      • Fixed inherited Amazon Bedrock dropping and failing to replay opaque redacted reasoning from non-Anthropic models (#8314 by @seiji).
      • Fixed inherited Z.AI Coding Plan models deriving incomplete reasoning-effort metadata, including missing GLM-5.3 low, high, and max levels (#8336).
      • Fixed inherited DeepSeek V4 Flash on OpenCode and OpenCode Go omitting its supported low thinking level (#8181 by @tianshuang).
      • Fixed inherited Azure OpenAI Responses ignoring toolChoice in provider-specific stream requests.
      • Fixed inherited Amazon Bedrock response hooks receiving only a synthesized request id instead of the raw response headers (#8234).
      • Fixed inherited Kimi usage reporting so top-level cached_tokens count as cache reads instead of normal input tokens (#8075).
      • Fixed inherited Google custom models ignoring thinkingLevelMap, which dropped extended thinking controls (#8135).
      • Fixed writes to auth.json and models-store.json overriding administrator-managed file permissions and ACLs (#7779).
      • Fixed UTF-8 BOM markers preventing frontmatter and user configuration files from loading (#8337).
      • Fixed invalid settings files being easy to miss during interactive startup by rendering warnings with the file path inside the TUI (#7829).
      • Fixed the subagent example repeatedly prompting before running project-local agents in trusted repositories (#8261).
      • Fixed npm package update checks treating older registry versions as available updates, preventing pi update from downgrading already-newer installed packages (#8226).
      • Fixed built-in llama.cpp models disappearing from /model when /llama refreshed a configured server under PI_OFFLINE, and included idle-slept sleeping router models in the selectable catalog (#8167).
      • Fixed pi.registerFlag() accepting default values that do not match the declared flag type (#8064).
      • Fixed Z.AI Coding Plan defaults referencing the removed GLM-5.1 model (#8096).
      • Fixed repeated ambiguous truncated-response recovery being mislabeled as context overflow (#8130).
      • Fixed duplicate fullscreen right-click paste in VS Code-based terminals on Windows (#8186).
      • Fixed inherited padded text exceeding narrow terminal widths (#8252).
      • Fixed inherited wrapped Markdown table links leaking color into borders and neighboring cells, including tables inside blockquotes (#8335).
      • Fixed llama.cpp login guidance to direct users to /llama before /model when no local models are loaded (#8203).
      • Fixed hung pi.dev model catalog requests consuming the entire refresh deadline without retrying (#8198).
      • Fixed inherited Xiaomi model catalogs listing shut-down MiMo V2 models in /model and --list-models (#8187).
      • Fixed branch summary entries recording the navigation destination in fromId instead of the pre-navigation source leaf.
      • Fixed threshold auto-compaction being skipped when providers omit streaming usage data (#8328).
      • Fixed dash-prefixed prompts being parsed as options by supporting -- as an end-of-options delimiter (#7269).
    8. 🔗 Barre/ZeroFS v2.3.1 release

      What's Changed

      • Bump client and transport crates to 0.3.0 by @Barre in #603
      • Update distro kernel package targets by @github-actions[bot] in #604
      • Kernel client recovery fixes by @Barre in #605
      • Fix shutdown task lifecycle by @Barre in #606

      Full Changelog : v2.3.0...v2.3.1

    9. 🔗 r/Harrogate Long shot rss

      Hey folks, wondering if there's a hero amongst you.. My wife and I borrowed our friends Snoo bassinet for our new baby girl. Last night Dad (me) managed to somehow catch the power cord on a door latch and irreparably damage it. Subsequently our daughter has enthusiastically made it known that she is not satisfied with the new sleeping arrangements and is now refusing to settle unless one of us holds her.

      The replacement is on its way but won't be here for a few days, so putting this out there in the hopes that someone in Harrogate has a Snoo they aren't currently using and would be willing to lend the power supply from for a few days. It would be returned with boundless gratitude and a nice bottle of something we'd try to resist opening ourselves first..

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

    10. 🔗 HexRaysSA/plugin-repository commits sync repo: +1 plugin, +1 release rss
      sync repo: +1 plugin, +1 release
      
      ## New plugins
      - [ida-func-call-rank](https://github.com/uky007/ida-func-call-rank) (0.1.1)
      
    11. 🔗 MetaBrainz GSoC 2026: Compose Multiplatform Migration of ListenBrainz-Android rss

      Hi everyone,

      I'm Nirvan Jain (nirvan_jain on IRC, nirvan73 on GitHub), I’m currently a pre-final year student at IIIT Jabalpur. This summer I was offered this opportunity to work with MetaBrainz through Google Summer of Code on migrating the ListenBrainz android app to Kotlin Multiplatform and Compose Multiplatform, so that most of the same codebase can eventually run on iOS under the mentorship of Jasjeet Singh (jasje on IRC).

      This post covers what the project was, everything I worked on, the parts that went wrong, what's still left, and what the summer was actually like.

      How I got here

      I started contributing to ListenBrainz in December 2025. It was the first external open source project I'd worked on. Everything before that was coursework, side projects, or intra-college events.

      My first PRs were small work which helped me a lot to understand the codebase from the ground, the shimmer effect across the Feed and Profile tabs, then extending search to cover playlists, artists, albums and tracks, plus some bug fixes. Small changes, but they taught me the codebase and how review works when nobody knows you and the code has to stand on its own.

      I picked ListenBrainz because music is a constant in my day, whether I'm coding or not, and because I like that it treats your listening history as something you own.

      The Problem

      ListenBrainz has an Android app but no iOS version. A previous Swift attempt just duplicated the codebase, so every bug fix had to be written twice.

      This project solves that by migrating to Kotlin Multiplatform, one shared codebase for both platforms. A large part of the work is replacing Android- only libraries with multiplatform equivalents across navigation, dependency injection and paging. Media playback is the harder case. ExoPlayer and WorkManager are tied too closely to Android to share, so both go behind common interfaces, with ExoPlayer on Android and AVPlayer on iOS underneath.

      Getting this foundation right is what makes the rest cheap. Once it's in place a feature or a bug fix is written once, instead of the same business logic being implemented twice and two apps being kept in sync by hand.

      One thing genuinely can't move. The notification listener that reads what's playing in Spotify depends on an Android-only permission, and iOS has no equivalent to offer. Those screens stay Android-only. Everything else runs from one codebase on both platforms.

      Project overview

      ListenBrainz is a platform for tracking your listening history, sharing what you're into, and building a community around music. The Android app is its mobile face, and this project was about rebuilding its foundations so that face isn't Android-only forever.

      The main goals were:

      • Shared logging with Kermit - Replacing the Android-only logger, with file writing and log submission split across platform implementations (#742)
      • Realtime events in the shared module - Moving off the Android-only socket.io-client so listen and playing_now work from commonMain on both platforms (#731)
      • ViewModels, repositories and the BrainzPlayer & ListensSubmission database into shared - Roughly twenty separate migrations, on top of the KMP lifecycle dependencies (#743 and #744 through #765)
      • A shared work manager - with listen submission and its data models and utilities moved into the shared module (#767)
      • Permissions in shared - with configuration for both Android and iOS (#770)
      • Screen migration - Starting with onboarding, which is where I am now

      Each of these gets its own section below, with what broke, what review caught, and where I diverged from the original proposal.

      Community bonding

      I spent bonding mostly auditing rather than coding. The shared module scaffold and the DataStore work already existed, so the useful question wasn't how to start but what exactly was still Android-only and what would replace each thing.

      That meant going through the dependency list one by one. The logger, socket.io, WorkManager, ExoPlayer, Accompanist, Lottie, the shimmer library, the WebView clients, the paging setup. For each one: is there a KMP-compatible replacement, does it need an interface with two implementations, or can it simply not exist on iOS and therefore has to stay Android-only forever?

      I also discussed with my maintainer and agreed on a migration order during this period. We settled on logging and sockets first, then ViewModels, then services and background work, then UI, so that each layer already had its dependencies migrated by the time it needed them.

      Coding Period

      Shared Logging: replacing the Android logger with

      Where it started

      The app used com.limurse.logger (Logger-Android) behind a thin Log interface:

      // app/util/Log.kt — before interface Log { fun e(message: Any?, tag: String? = null) fun d(message: Any?) fun w(message: Any?) companion object : Log { override fun e(message: Any?, tag: String?) = Logger.e(tag, msg = message.toString()) override fun d(message: Any?) = Logger.d(msg = message.toString()) override fun w(message: Any?) = Logger.w(msg = message.toString()) } }
      

      Two problems. It's a JVM library, so it can't cross into commonMain. And because Log is a companion object called statically from many files, every single one of those files had a hard, untestable dependency on Android. This one had been started by another contributor PR - #728 before I picked it up, the Kermit swap and a first pass at file logging existed.

      What I did was take it apart and rebuild the layering, because the first version had every platform's DI wiring duplicated and the file writer had almost all of its logic sitting in androidMain even though most of it was plain Kotlin.

      The interface stays, the backend changes

      The first decision was to keep theLog interface and its companion. It's called from everywhere, changing the call sites would have made the diff unreadable and the review impossible. So Log moved to shared/util/Log.kt, kept its shape, gained i/v/log and a throwable parameter, and started delegating to a Kermit Logger pulled out of Koin.

      interface Log { fun e(message: Any?, tag: String? = null, throwable: Throwable? = null) fun d(message: Any?, tag: String? = null) // w, i, v, log ... companion object : Log, KoinComponent { private val logger: Logger = get() override fun e( message: Any?, tag: String?, throwable: Throwable? ) { logger.withTag(tag ?: "ListenBrainz").e(throwable) { message.toString() } } // ... } }
      

      KoinComponent on the companion is what makes a static-looking API injectable. The object stays a singleton, but what it is now decided by DI, per platform.

      Splitting the file writer

      The old file writer was one Android class doing five things: formatting timestamps, collecting device metadata, serialising writes, formatting log lines, and actually touching the filesystem. Only the last one is platform- specific.

      So SharedFileLogWriter is an abstract class in commonMain that extends Kermit's LogWriter and does everything except the write. Click to see the implementation

      abstract class SharedFileLogWriter(private val buildConfig: BuildInfo): LogWriter() { private val loggerQueue = Channel<String>(capacity = Channel.UNLIMITED) protected fun initBlock() { loggerScope.launch(Dispatchers.IO) { for (entry in loggerQueue) writeLineToFile(entry) } } override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) { val level = when (severity) { /* Verbose -> "VERBOSE", ... */ } writeToFile(message, tag, level) throwable?.let { writeToFile(it.stackTraceToString(), tag, level) } } protected abstract suspend fun writeLineToFile(entry: String) }
      

      The Channel is the interesting bit. Logging happens on whatever thread the caller is on, and file I/O has to be serialised or you get interleaved half- lines. An unlimited channel consumed by a single Dispatchers.IO coroutine gives you a non-blocking log() call and a strictly-ordered writer, with no locks.

      As for the iOS implementation, I haven't added it in this PR yet. I wanted to get the base shared setup merged first so the rest of the PRs isn't blocked on moving logger-dependent files into the shared module. Once every file involving logger in their code is migrated over, I’ll follow up with the actual iOS file writer in a separate PR.

      I made one BuildInfo, a plain data class which consists of -> application id, version code, version name, build type, bound in DI by the app. Since commonMain cannot see BuildConfig, so anything that wants build metadata takes a BuildInfo instead.

      Log submission

      "Submit logs" means:- zip everything in the log directory. I have created an interface named LogSubmitter, a contract in common which only have one method as:-

      interface LogSubmitter { suspend fun submitLogs() }
      

      AndroidLogSubmitter is one of the extension of LogSubmitter in androidMain which zips the .txt files, exposes the archive through FileProvider, and fires an ACTION_SEND chooser. Two review-driven fixes landed here: -

      • Passed the PlatformContext as applicationContext through the parameters of the class
      • Handled thread switching explicitly, file compression runs on Dispatchers.IO, while building and launching the chooser intent switches back to withContext(Dispatchers.Main)

      The iOS side (IosLogSubmitter, IosFileLogWriter) is not implemented yet and will be added in a follow-up.

      The DI shape, and what review changed

      My first version had a SharedAppModule.android.kt and a SharedAppModule.ios.kt for all the platform specific modules, each defining a full Koin module. Review corrected this:- it means every new binding has to be added twice, and the two modules drift. The fix was to have one module in commonMain under platformModule and make only the factories expect/actual like provideLogger. Click to see the example of the same

      // commonMain/Platform.kt expect fun provideLogger(buildInfo: BuildInfo): Logger expect fun provideLogSubmitter(buildInfo: BuildInfo): LogSubmitter // commonMain/di/SharedAppModule.kt val platformModule = module { single<Logger> { provideLogger(get<BuildInfo>()) } single<LogSubmitter> { provideLogSubmitter(get<BuildInfo>()) } // ... }
      
      
      // androidMain/Platform.android.kt actual fun provideLogger(buildInfo: BuildInfo): Logger { val writers = mutableListOf(platformLogWriter()) applicationContext.getExternalFilesDir(null)?.let { dir -> val logDir = File(dir, ANDROID_LOG_DIR_NAME).apply { mkdirs() } writers.add(AndroidFileLogWriter(logDir.path, buildInfo)) } return Logger( StaticConfig( minSeverity = Severity.Debug, logWriterList = writers ), tag = "ListenBrainz" ) }
      
      
      // iosMain/Platform.ios.kt actual fun provideLogger(buildInfo: BuildInfo): Logger { val writers = mutableListOf(platformLogWriter()) (NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, true ).firstOrNull() as? String)?.let { writers.add(IosFileLogWriter(it, buildInfo)) } return Logger( StaticConfig( minSeverity = Severity.Debug, logWriterList = writers ), tag = "ListenBrainz" ) }
      

      Same module, same binding, one line of difference per platform. This became the template for every other platform-backed dependency in the project -RemotePlaybackHandler, PermissionHandler, ListensRepository, the Room builder. Platform.kt is now the single file where you can read off everything the shared module needs a platform to supply.

      Realtime Events in the Shared Module

      "Listening Now" is fed by a Socket.IO connection to listenbrainz.org that pushes two events, listen and playing_now. The existing implementation used io.socket:socket.io-client, which is JVM-only and pulls in org.json:

      // before — app module private val socket: Socket = IO.socket( "https://listenbrainz.org/", IO.Options.builder().setPath("/socket.io/").build() ) socket.on("playing_now") { json.decodeFromString<Listen>(it[0] as String) }
      

      Attempt one: Engine.IO by hand

      Ktor's websocket client is multiplatform, so my first instinct was to drop the Socket.IO library entirely and talk the protocol directly. Socket.IO over websockets is really Engine.IO framing:

      My first implementation did it the hard way. I wrote the Engine.io and Socket.io protocol handling by hand…. on top of ktor-client-websockets.

      • manual handshake with the 40 frame to open the session
      • heartbeat by answering every incoming 2 with a 3
      • and event subscription parsing JSON payloads out of the frames myself.

      Click to see the implementation

      // the version that worked, and that I threw away :( httpClient.webSocket("wss://listenbrainz.org/socket.io/?EIO=4&transport=websocket") { send(Frame.Text("40")) send(Frame.Text("""42["json",{"user": "$username"}]""")) for (frame in incoming) { val data = (frame as? Frame.Text)?.readText() ?: continue when { data == "2" -> send(Frame.Text("3")) // heartbeat data.startsWith("42") -> { val array = json.parseToJsonElement(data.removePrefix("42")) as JsonArray when (array[0].jsonPrimitive.content) { "playing_now", "listen" -> trySendBlocking( json.decodeFromString<Listen>(array[1].jsonPrimitive.content) ) } } } } }
      

      It connected. It received listens. It was also, on reflection a bad idea, reimplementing a wire protocol inside an application repository means the app now owns a protocol implementation it has to maintain and debug and keep in sync with a spec it doesn't control. Thats why I have to switch to using a KMP SocketIO - OSS library.

      Attempt two:

      I switched to com.piasy:kmp-socketio, which is a real Socket.IO client for KMP and crucially lets you hand it your own KtorHttpClient, so realtime traffic goes through the same configured, logged, engine-agnostic client as everything else. Click to see the implementation

      class SocketRepositoryImpl( 
          private val httpClient: HttpClient, 
          private val json: Json, 
          private val logger: Log = Log, 
      ) : SocketRepository { 
          override fun listen(usernameProvider: suspend () -> String) = callbackFlow { 
              val username = usernameProvider() 
              val options = IO.Options().apply { 
                  httpClient = this@SocketRepositoryImpl.httpClient 
                  transports = listOf("websocket") 
              }
      
              var activeSocket: Socket? = null 
              IO.socket("https://listenbrainz.org", options) { socket -> 
                  activeSocket = socket 
                  socket.on(Socket.EVENT_CONNECT) { 
                      socket.emit("json", buildJsonObject { put("user", username) }) 
                  } 
                  listOf("playing_now", "listen").forEach { event -> 
                      socket.on(event) { data -> 
                          runCatching { 
                              val payload = data.firstOrNull()?.toString() ?: return@on
      
                              trySendBlocking(json.decodeFromString<Listen>(payload)) 
                          }.onFailure { logger.e("SocketRepository: $event error ${it.message}") } 
                      } 
                  } 
                  socket.open() 
              }
              awaitClose { activeSocket?.close() } 
          } 
      }
      

      The callbackFlow + awaitClose shape survived the rewrite unchanged, which is the nice thing about having had a decent boundary in the first place, SocketRepository.listen() returns a Flow<Listen?>, and neither the ViewModel nor the UI ever knew which library was underneath.

      KMP Lifecycle, ViewModels and Repositories

      This is where most of the raw hours went and it's the least interesting part to write about, which is exactly why it's worth writing about. This is the bulk of the project by volume: ~20 PRs, one feature each.

      The dependency swap that unlocks everything

      AndroidX lifecycle ships two artifact families: lifecycle-viewmodel-ktx (Android-only) and lifecycle-viewmodel (multiplatform since 2.8). They have the same API. Switching the whole project to the base artifacts is a three- line diff in the version catalog and is the entire technical prerequisite for shared ViewModels.

      The KMP lifecycle dependencies had to land first so migrated ViewModels had a ViewModel base and a viewModelScope that exist on both platforms. Then, one at a time: Settings, Artist, Album, Song, Playlist ,etc ViewModels, the remote playback handler, the BrainzPlayer database, and SocialRepository.

      One PR each, about twenty of them.

      After migrating the viewmodels, repositories and network services to shared module, they needed to be wired up through Koin. I organized these dependencies into dedicated shared modules so the platform app module could easily consume them without caring about their internal package structure:

      • SharedViewModelModule -> Registers all shared ViewModels.
      • SharedRepositoryModule -> Binds the shared repository layer.
      • SharedNetworkServiceModule -> Configures shared API clients and network services.
      • SharedAppModule -> Handles core utility implementations (like RemotePlaybackHandler).

      Retrofit to Ktorfit, OkHttp to engine-agnostic

      Services became Ktorfit interfaces, which look almost identical:

      interface ListensService { 
          @GET("user/{user_name}/listens") 
          suspend fun getUserListens( 
              @Path("user_name") username: String, 
              @Query("count") count: Int, 
              @Query("max_ts") maxTs: Long? = null, 
          ): Listens
      
          @POST("submit-listens") 
          suspend fun submitListen(@Body body: ListenSubmitBody?): PostResponse 
      }
      

      I created an expect fun of the engine with its platform-specific actual fun implementation. Click to preview

      // commonMain 
      expect fun getPlatformNetworkEngine(): HttpClientEngineFactory<*> 
      expect fun configPlatformEngine(config: HttpClientEngineConfig, context: PlatformContext)
      
      // androidMain — OkHttp, plus Chucker in debug builds 
      actual fun getPlatformNetworkEngine(): HttpClientEngineFactory<*> = OkHttp 
      actual fun configPlatformEngine(config: HttpClientEngineConfig, context: PlatformContext) { 
          if (config is OkHttpConfig && BuildKonfig.DEBUG) {
              config.addInterceptor(ChuckerInterceptor(context)) 
          }
      }
      
      // iosMain — Darwin 
      actual fun getPlatformNetworkEngine(): HttpClientEngineFactory<*> = Darwin
      

      The awkward cases

      Not everything I migrated went cleanly. Those are the parts I found interesting, so I'm giving them the most space here.

      String resources in ViewModels

      Several ViewModels emit user-facing success messages by resource id, and commonMain has no R. I could have moved res/, but that's a much larger change than I wanted to make inside a feature PR, so I wrote small provider interfaces keyed by enum instead:

      // shared/util 
      enum class StringResource { 
          TRACK_ADDED_SUCCESSFULLY, 
          PLAYLIST_DUPLICATED_SUCCESSFULLY, 
          /* ... */ 
      }
      
      interface StringProvider { 
          fun getString(res: StringResource): Int 
      }
      

      The app implements it as a one-line when mapping enum to R.string.*, and I bound it in Koin. I wrote three of these: StringProvider, DrawableProvider, ArrayProvider. I made them deliberately boring. They let me migrate a feature today and let someone migrate the resources later, on their own schedule.

      Repositories that really are platform-specific

      ListensRepository needs getPackageLabel(pkgName), which turns com.spotify.music into "Spotify". That's a PackageManager call with no iOS analogue.

      Rather than leak it into common, I kept the interface common and picked the implementation with an expect fun, so I ended up with an AndroidListensRepositoryImpl and an IosListensRepositoryImpl sharing a common parent. I did the same thing for RemotePlaybackHandler, which is Spotify App Remote on Android and a stub I wrote on iOS.

      The BrainzPlayer database

      My proposal listed the local databases as a migration target, and i did the work. Room Multiplatform, schema in commonMain, builder behind expect. The PR consists the moving of four entities, four DAOs, TypeConverter and Transformer across, and deleting the app-side database and converter files outright.

      The boundary turned out narrower then I expected, Only the builder is platform-specific. The driver, the migration list and build() all stay in common DI, so the part I'd most hate to see diverge across platforms is written exactly once.

      // commonMain - the entire platform surface of the database 
      expect fun getBrainzPlayerDatabase(context: PlatformContext): RoomDatabase.Builder<BrainzPlayerDatabase>
      
      single<BrainzPlayerDatabase> {
          getBrainzPlayerDatabase(get())
              .setDriver(BundledSQLiteDriver())
              .addMigration(Migrations.MIGRATION_1_2,Migrations.MIGRATION_2_3)
              .build()
      }
      

      Android supplies a Context and getDatabasePath().absolutePath. iOS supplies a path from NSFileManager. One line of diff , each platform. BundledSQLiteDriver() in common means both platforms ship the same SQLite built. This is the same "one module in common, only the factories are expect/actual", temp late I settled on during the logging migration, and it held up on a much heavier target.

      Room's KMP mode also made me do something I'd never done on Android, The database object needs a generated constructor. Click to preview

      @Database(entities = [SongEntity::class, AlbumEntity::class, ArtistEntity::class, PlaylistEntity::class], version = 3) 
      @TypeConverters(TypeConverter::class) 
      @ConstructedBy(BrainzPlayerDatabaseConstructor::class) 
      abstract class BrainzPlayerDatabase : RoomDatabase() { 
          /* ... */ 
      }
      
      @Suppress("KotlinNoActualForExpect") 
      expect object BrainzPlayerDatabaseConstructor : RoomDatabaseConstructor<BrainzPlayerDatabase>
      

      That @Suppress isn't style. KSP generates the actual, but the Kotlin compiler goes looking for it before KSP has run.

      A drawable resource living in a database column

      The interesting problem in this PR was PlaylistEntity.art. It was an @DrawableRes Int, and commonMain has no R:

      // shared — no idea what a drawable is 
      val art: String = "ic_queue_music"
      
      // app 
      fun getPlaylistArtMapper(art: String): Int = when (art) { ... }
      

      That's the same resource-indirection trick as my StringProvider and DrawableProvider, but with two costs those don't carry. The column is persisted, so changing its type forced a schema migration. And it's stringly- typed, so the compiler stops checking my work. Both costs came due. The migration showed up in review, the lost type safety showed up right after.

      SQLite can't ALTER COLUMN, so MIGRATION_2_3 rebuilds the table. The part I actually thought about is that it back-fills the new keys from the row ids rather than defaulting every playlist to the generic icon, and then repairs sqlite_sequence so AUTOINCREMENT doesn't restart into ids that still exist.
      Click to see the SQLite code

      INSERT INTO `PLAYLISTS_TEMP` (`id`,`title`,`items`,`art`) 
      SELECT `id`, `title`, `items`, 
          CASE WHEN `id` = -1 THEN 'ic_queue_music_playing' 
               WHEN `id` = 0 THEN 'ic_liked' 
               ELSE 'ic_queue_music' END 
      FROM `PLAYLISTS`
      

      Where my plan changed

      Two weeks after this merged, the plan changed underneath it, Since the local music player used ExoPlayer, an android specific component and was tightly coupled to UI components and viewModels that shouldn't have been concerned with, complicating our migration to CMP.

      So my migration turned into a deletion. including the database I had just finished moving. That meant the app also had to clean up after itself for users upgrading from a version that had it:

      private fun cleanupBrainzPlayerResources() { 
          context.getDatabasePath("brainzplayer_database") 
              .takeIf { it.exists() } 
              ?.let { context.deleteDatabase("brainzplayer_database") 
          }
      
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 
              (getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager) 
                  ?.deleteNotificationChannel("Music") 
          } 
      }
      

      Shared work manager

      This was the one of the trickiest part of my project and the PR for the same is under review and not merged yet , because listen submission is the one piece of the app that is inherently Android (it works by listening to other apps' media notifications) and yet almost none of its logic actually is.

      Here's the pipeline I ended up with:

      ListenSubmissionService (app, NotificationListenerService — Android-only entry point)
      
      ListenSessionListener (shared/androidMain - MediaSessionManager callbacks)
      
      ListenServiceManagerImpl (shared/androidMain — whitelist)
      
      ListenSubmissionState (shared/androidMain — timers, builds PlayingTrack, schedules work)
      
      ListenSubmissionWorker (shared/androidMain — KMP worker: POST, or persist for retry)
      

      I left only the first box in :app, and only because NotificationListenerService has to be declared in the app manifest. I moved everything downstream of it.

      From androidx.work to KmpWorkManager

      For background work I replaced Android's WorkManager with KmpWorkManager, updated ListenSubmissionWorker to match, and removed the native WorkManager DI wiring. This is one of the places where i diverged from my proposal, which described hand-rolling a BackgroundTaskScheduler interface with WorkManager on Android and BGTaskScheduler on iOS. Using an existing KMP library meant less code to write and less to maintain for the same result.

      I annotate my workers and let KSP discover them:

      @Worker("ListenSubmissionWorker") 
      class ListenSubmissionWorker : AndroidWorker, KoinComponent {
      
          private val appPreferences: AppPreferences by inject() 
          private val repository: ListensRepository by inject() 
          private val pendingListensDao: PendingListensDao by inject()
      
          override suspend fun doWork(input: String?, env: WorkerEnvironment): WorkerResult { /* ... */ } 
      }
      

      I schedule by string name and JSON payload, because a KMP scheduler can't take a Class<*> or an androidx Data:

      scheduler.enqueue( 
          id = "listen-${playingTrack.id}-${System.currentTimeMillis()}", 
          trigger = TaskTrigger.OneTime(initialDelayMs = 0), 
          workerClassName = "ListenSubmissionWorker", 
          inputJson = Json.encodeToString(ListenWorkerInput(playingTrack, ListenType.SINGLE)), 
          constraints = Constraints(requiresNetwork = true), 
      )
      

      so my input model is just:

      @Serializable 
      data class ListenWorkerInput(
          val track: PlayingTrack, 
          val listenType: ListenType
      )
      

      plus an explicit KmpWorkManager.initialize(context, AndroidWorkerFactoryGenerated()) alongside startKoin initialising KMP WorkManager.

      Then ListenSubmissionState moved into the shared module under androidMain, rewired to use the shared notification manager and shared work manager, with the data models and utility functions it depends on coming across with it. ListenSubmissionService in the app module was updated to consume the shared utilities and services, and the Koin registry updated accordingly.

      Permissions in the shared module

      The PR for the same is under review and not merged yet. Last year's contributor built a PermissionEnum that put all permission logic in one place: title, rationale, permanently-declined copy, image, minSdk, maxSdk, and the raw permission string. I think it's a genuinely good design, and it made the onboarding screens trivial to write. It's also, top to bottom, Android:

      enum class PermissionEnum( 
          val permission: String, // "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" 
          val image: Int, // R.drawable.* 
          val minSdk: Int, // Build.VERSION_CODES 
          val maxSdk: Int? = null, 
      )
      

      Every field except the copy is Android-specific. I couldn't make a single shared enum work, and I didn't want to duplicate the whole thing per platform.

      Splitting the enum without losing the enum

      What I settled on was making the contract an interface, and letting each platform contribute its own enum implementing it. Click to see the implementation

      // commonMain 
      interface AppPermission { 
          val id: String 
          val title: String 
          val permanentlyDeclinedRationale: String 
          val rationaleText: String 
          val image: DrawableResource // enum, not an Int 
      }
      
      // commonMain — permissions that exist everywhere 
      enum class SharedPermissionEnum(..) : AppPermission { 
          SEND_NOTIFICATIONS( 
              id = "send_notification", 
              title = "Send Notifications", 
              rationaleText = "Needed to send updates on activity, recommendations, and system alerts…", 
              image = DrawableResource.IC_NOTIFICATION, 
          ) 
      }
      
      // androidMain — permissions that only exist here 
      enum class AndroidPermissionEnum(
          ..., 
          val systemPermission: String,
          val minSdk: Int, 
          val maxSdk: Int? = null
      ) : AppPermission { 
          READ_NOTIFICATIONS(systemPermission = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE", minSdk = 33, ..), 
          BATTERY_OPTIMIZATION(systemPermission = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", minSdk = 23, ..), 
      }
      

      I made image a DrawableResource enum resolved by the DrawableProvider, so my shared code can name an icon without knowing what an R.drawable is. That left me one problem. SEND_NOTIFICATIONS lives in common but still needs an Android manifest string and a minSdk on Android.
      I solved it with an extension in androidMain that supplies the Android facts for shared constants

      The handler

      I moved everything behavioural behind one interface:

      interface PermissionHandler {
      
          fun getAllRelevantPermissions(): List<AppPermission> 
          fun getPermissionsForPermissionScreen(): List<AppPermission> 
          fun isPermissionApplicable(permission: AppPermission): Boolean
      
          suspend fun isGranted(permission: AppPermission): Boolean 
          fun storageKey(permission: AppPermission): String
      
          suspend fun requestPermission( 
              permission: AppPermission, 
              activity: Any? = null, 
              permissionRequestedOnce: List<String> = emptyList(), 
              dangerousPermissionLauncher: (permission: String) -> Unit = {}, 
          ): Boolean? 
      }
      

      activity: Any? is the one compromise in my whole design, and I want to be upfront about it. commonMain cannot name Activity, and Android's rationale APIs (shouldShowRequestPermissionRationale) genuinely require one. So my Android implementation casts (activity as? Activity) and degrades gracefully to a FLAG_ACTIVITY_NEW_TASK intent when it's null.

      I kept all of last year's special cases in the Android implementation, including the two "permissions" that aren't runtime permissions at all.

      My iOS handler is much smaller, and that's the point. It only has to answer for the permissions iOS actually has, which is just SEND_NOTIFICATIONS since iOS sandbox security and privacy rules don't allow the other permissions anyway.

      One detail which is storageKey(). Whether a permission has been requested once is persisted in DataStore, and the obvious key is the enum constant's name, which means renaming a constant would silently reset everyone's onboarding state. On Android I use the system permission string instead, which is stable by definition. On iOS I use the explicit id field, and that's the only reason I put id on the interface at all.

      Screen migration: onboarding

      I'm currently working through the UI layer starting with onboarding.

      Onboarding is a reasonable place to start for two reasons. It's the first thing a user sees, so regressions are visible immediately and get caught fast.

      The back half is different. Listen submission setup and the third-party app selector both depend on NotificationListenerService, so by the same reasoning as above those screens stay Android-only and won't exist in an iOS build. The shared onboarding graph walks both platforms through the first three screens, Android then hands off to its own two, and iOS ends the flow there.

      A side quest: token login

      While working on the auth screens, I added a direct-token login path alongside the existing WebView flow. Due to ongoing server-side auth migrations, we needed a more direct and temp, resilient fallback while keeping the door open for multiplatform support. Click to see the implementation

      fun submitToken(onLoginFinished: () -> Unit) {
          val token = uiState.value.token.trim()
          if (token.isBlank()) return setError("Token cannot be empty")
          if (uiState.value.loginState is TempLoginState.VerifyingToken) return
      
          viewModelScope.launch {
              val result = withContext(ioDispatcher) { listensRepository.validateToken(token) }
              val validation = result.data
              if (result.isSuccess && validation?.valid == true) {
                  appPreferences.username.set(validation.username ?: "")
                  appPreferences.lbAccessToken.set(token)
                  onLoginFinished()
              } else {
                  setError(validation?.message ?: "Invalid token. Please check and try again.")
              }
          }
      }
      

      What's left

      I'd rather say this plainly than bury it. My proposal was ambitious and several sections of it are still open:

      1. iOS file logging and log submission are TODO(). The shared machinery (queueing, formatting, startup metadata, the LogSubmitter contract) is all done and platform-neutral, the two iOS leaves are stubs.
      2. System bar control away from accompanist-systemuicontroller.
      3. Browser and external URL handling via expect/actual.
      4. Lottie to Compottie.
      5. Shimmer to a KMP shimmer implementation.
      6. Onboarding screen migration and all other composable screens migration to CMP, started but not complete.
      7. Testing migration to Kotlin Test with shared tests in commonTest

      Two reasons for the gap. The foundational work, logging, sockets, ViewModels, notification and work manager, permissions, took considerably longer than I estimated, largely because each piece went through multiple review rounds that improved it but slowed it down. And the OAuth login change arrived mid-project and cost time I hadn't planned for.

      Post-GSoC plan

      I 'm not going anywhere. The unfinished list above is my roadmap, roughly in this order.

      • The UI dependency cluster:- WebView, system bars, browser handling, Lottie, shimmer. These are individually small and mostly independent, so they can be done steadily instead of needing a large block of time.

      • Then finishing the screen migration, continuing from onboarding through the home navigation shell, settings, dashboard and feed.

      • Then the testing migration, moving platform-independent tests into commonTest so the shared module has an actual safety net. This one matters most for whoever picks the project up after me. A migration without tests is a liability handed to the next person.

      • And eventually the real OAuth login flow, replacing the temporary token-paste screen with something users shouldn't have to put up with.

      Outside the migration I still want to build the playlist sorting feature from my proposal, and keep fixing bugs.

      What I learned

      The clever solution was usually the wrong one. My instinct on the socket migration was to build the impressive thing, hand-rolled protocol handling and all. The right answer was the boring maintainable one, and I now read the urge to be clever as a sign I haven't thought hard enough about who maintains this in two years.

      Almost everything I actually learned came out of review. Thread safety, DI hygiene, why println doesn't belong in production code. Having changes requested three times on one PR taught me more than any PR that merged first try, though getting comfortable with that, treating requested changes as information rather than as a verdict, took a few weeks.

      Final Thoughts

      I am truly grateful for this amazing opportunity and thanks Jasjeet for his constant guidance and reviewing an enormous amount of my code carefully and for pushing back every time I was overcomplicating something. A fair amount of this post is his review comments with more words around them.

      More than anything, this project showed me the gap between code that works and code that lasts. I learned to think in terms of clean contracts, edge cases, and long-term maintainability rather than quick fixes. It gave me real confidence in navigating complex codebases, embracing tough reviews, and contributing to industry-scale projects with purpose.

      And Finally, thanks to the MetaBrainz community for their support. This was my first serious open source project and it's a welcoming place to have started. I'll be around well past this summer.

    12. 🔗 Armin Ronacher Anger, Anxiety and Agency rss

      Sean Goedecke wrote a post arguing that you should never be angry at work — a post with which I strongly agree. Anger can be a useful signal, but being angry at work rarely improves the situation. More often, it makes life worse for the people around you, many of whom have no more power over the source of your anger than you do. I did learn that lesson, but it did not come naturally. One thing in particular that I learned is that in a company there is a shared vision, and if you don't agree with it and are not in a position to change it, you should not start a mutiny, not even a small-scale one. Nothing good comes from that.

      In the discussion around that topic, one of the most upvoted comments on the Lobsters thread asked a question I had to think about quite a bit:

      How can you work in tech right now and not be angry?

      In the context of the thread, this was clearly also about AI and agents. For me, the emotions I would expect in tech vis-a-vis these new developments are disorientation and anxiety, but not anger.

      Anxiety as an emotion does not require someone to blame. Right now, I find it reasonable to feel anxious about an uncertain future. Who knows what our professions will turn into and what kind of world my kids will find themselves in when they enter the workplace? And if you've been in the industry for a long time, will the skills you've spent years acquiring still matter?

      But anger is different from anxiety because anger needs to be directed somewhere. The feeling of anger suggests that somebody or something is doing something to you.

      Who are you going to be angry at and why are you angry in the first place? One narrative that is pretty pervasive is that if AI will usher in productivity gains, those gains are going to benefit companies rather than employees. And well at least someone at Meta wants that. Yet I also find that plenty of people in leadership positions express doubt about AI. They see that an increasing share of their costs is being funneled directly to some large AI labs. They express worries about what will happen to their data and whether these large companies will step into their space instead of being partners.

      My answer to the question of how you can not be angry in tech is that it's by no way the most only possible feeling. First of all, instead of being angry, you can simply be unsure. The feeling of uncertainty is a much more productive emotional state because it can lead to curiosity. Even if you don't find what's happening right now exciting, you can at least find it interesting. We have access to magic machines, and we can poke at them and see what happens. The second way is to feel genuine excitement. Once you move beyond curiosity, you can come away with a newfound feeling of power and freedom. A lot of the gains from AI aren't turning into productivity gains that are reflected in company profits but they're showing up instead in the number of side projects shipped by everybody not on their company's time.

      The fact that this is happening shows us that owners and founders don't necessarily know what will happen. Ownership comes with agency, but it does not provide foresight, and this change is disorienting for everybody. I engage with plenty of people who project confidence in public and are much less certain in private. Many of them are placing bets, but they are talking with confidence about those bets, trying to keep their business afloat while the ground moves under them. They experience that uncertainty from a position where they can act on it, and they are often standing somewhere with a megaphone to get others on their side to improve their odds.

      I feel that contradiction myself: I am simultaneously tremendously excited, but I am also unsure what will happen next. I do not know what it will mean to be a programmer in the future, and, as the owner of a company, I am also not sure where the high ground will be when this all settles. Much of what I learned over the years is changing rapidly, including ideas I considered fundamental to my craft and business. Some days that feels liberating, but on others I wake up feeling like the ground is crumbling beneath me.

      Anxiety is an uncomfortable emotion because it acknowledges that you do not know what will happen and might not be able to stop it. On the other hand, anger can feel more actionable because, instead of saying "I don't know," you already have someone to blame. It turns a loss of control into a comforting story with a villain. But I feel that particularly when it comes to AI, it's easy to pick the wrong villain because of how disruptive the change is for everyone. Your engineering manager or leadership team might themselves feel uncertain about their future and just try to bolster their own confidence by projecting clarity and certainty.

      That does not mean there are no villains. When this all plays out, some will profit and many will not. I'm afraid we're completely ignoring the impact this has on society at large, the climate, and the balance of the world as a whole. As excited as I am about the technology, I worry about Europe's lack of ambition and growing dependence on other countries. I have a lot of complex thoughts about what we're doing as an industry right now.

      I don't know what the future of this industry will look like, and I don't know who will benefit from it and I don't think I'm alone with that. However I can only urge anyone who feels anger and looks for a villain right to instead remain curious instead. To be curious enough to understand what is changing, excited enough to experiment with it. And then, from what we learn, earn the right to decide when resistance is warranted and where to direct it.

  4. August 23, 2026
    1. 🔗 IDA Plugin Updates IDA Plugin Updates on 2026-08-23 rss

      IDA Plugin Updates on 2026-08-23

      New Releases:

      Activity:

      • AiDAPrivate
      • disrobe
        • 61e5c9ba: js: reuse module names across independent factories
        • 88fe49f3: php: recover fetch_is variable reads
        • d1bb0686: provision php 8.4 for op array differentials
        • 2e586db4: wasm: grade threaded auto output and json refusals
        • be5b597f: native: route boundary traps through every x86 leaf consumer
        • 7829a85c: php: recover variadic and named call arguments
        • d46764c4: native: refuse int3 at declared function boundaries
        • 77e35e38: jvm: require private unreferenced lambda helpers for declaration elision
        • c1a4eff1: as3: pin short-circuit and merge reconciliation boundaries
        • b72d90cb: php: recover list destructuring assignments
        • 179722ea: wasm: bound module source rendering
        • 5d1f819c: javascript: refuse unproven for-of subjects
        • 711c0d40: workspace: partition windows tests by package
      • headless_ida_9.4_claude_skill
        • c0603394: Revise README for Claude IDA skill version 3
        • 246e84aa: Harden Bash discovery when no initial IDA paths exist
        • e0098097: Document setup parameters, discovery, and v3 feature branch
        • ff99d97c: Bring Bash setup to parity with PowerShell parameters and discovery
        • 1f0cf229: Discover all local IDA 9.1-9.4 installs and prefer 9.4
        • eca3657e: Fix trailing IDA path separator breaking idapro imports
        • 5439c23b: Fix PowerShell setup path discovery crash
        • c944e974: Merge pull request #1 from shefben/feature/intelligence-speed-v2
        • eac7b12e: Document project-scoped global skill state
        • 48cfd960: Document global-skill project state isolation
        • b0deab5f: Record project identity in evidence databases
        • 6687bd42: Use project-scoped default graph database
        • 4e12bbb0: Namespace persistent state by project identity
        • 64630853: Document 3.0 intelligence and speed upgrade
        • 00d663de: Bump skill version for intelligence layer
        • 1242e77c: Wire Claude to new intelligence and speed layers
        • 0d434521: Make finding application transactional and verifiable
        • 9302edfc: Add planner runtime recovery indexer and investigation frontier
        • c54fceb1: Add multi-binary project graph
        • c2790090: Add SQLite evidence graph and contradiction tracking
      • ida-free-mcp
        • 6e08fd04: fix(release): build universal macOS binary and clean up its rpaths
      • ida-func-call-rank
        • 919d023e: Add Plugin Manager metadata for Hex-Rays repository
      • ida-nexus
        • 0b4e1ebc: Mark registered tools as essential in omp per default
      • twdll
        • 4e6c3d9d: test(military_force): ensure dynamic target bodyguard key in AppointC…
        • f985c8f5: feat(military_force): support custom bodyguard_key option in AppointC…
        • f4d5961f: feat(military_force): implement AppointCharacter method
        • 605f43dd: fix(tweakers): validate SetInt lookup result and preserve float value…
        • e4f0619f: docs(changelog): release version 0.10.0
        • b3385736: fix(core): reset lua stack before module registration in luaopen_twdll
        • e3613b37: refactor(tweakers): summarize tweaker and campaign variable restore l…
    2. 🔗 MetaBrainz GSoC 2026: Modernize search storage format for the MusicBrainz database rss

      Hello Everyone!

      I’m Junaid (fettuccinae), an undergraduate Computer Science student at MGIT in India. This summer, I returned to MetaBrainz for my second GSoC project, where I worked on modernizing the MusicBrainz search under the mentorship of @kartikohri13 and @bitmap.

      Project Overview: MusicBrainz uses Apache Solr for search queries. The previous implementation serialized all the response data into a single _store field. The response writers reads _store, unmarshal the XML into a MusicBrainz XML Metadata Format (MMD) object and then serialize it again as XML or JSON for the response. Few problems with this design are: 1. The indexer must construct a complete XML representation for every document, even though much of the same information is already available in normal Solr fields. 2. Most of the response data is stored in an opaque _store XML blob. This project focused on moving the fields from one _store XML blob into their own flat fields (and JSON strings for nested fields). The main goals of this project were: 1. Upgrade the Solr schema version from 1.5 to 1.7 2. Add fields (in configsets and indexer) to store all the data to be returned 3. Create response writers to return data from fields The proposal for this project can be found here. Result: This project replaced the _store blob with stored fields and JSON string fields across all 16 search entities. I reindexed both the old and new configurations using the sample MusicBrainz database, then force-merged the indexes before measuring them. I wrote a python script to measure their sizes using Solr's status API and compared them. The total core size went from 3.8 GiB to 2.8 GiB, saving around 1 GiB or 26%. Collection| Old core| New core| Difference% ---|---|---|--- annotation| 15.8 MiB| 14.8 MiB| 5.8% area| 7.9 MiB| 4.5 MiB| 42.9% artist| 131.3 MiB| 81.2 MiB| 38.2% cdstub| 94.0 MiB| 56.0 MiB| 40.5% editor| 5.6 MiB| 1.9 MiB| 65.6% event| 2.3 MiB| 1.4 MiB| 39.7% instrument| 542.8 KiB| 405.2 KiB| 25.4% label| 10.7 MiB| 6.3 MiB| 40.6% place| 6.2 MiB| 3.7 MiB| 40.1% recording| 3.0 GiB| 2.2 GiB| 26.1% release| 37.2 MiB| 25.9 MiB| 30.5% release-group| 42.1 MiB| 31.7 MiB| 24.7% series| 1.2 MiB| 624.5 KiB| 49.6% tag| 3.6 MiB| 1.6 MiB| 56.0% url| 65.7 MiB| 44.4 MiB| 32.3% work| 363.0 MiB| 307.1 MiB| 15.4% Total| 3.8 GiB| 2.8 GiB| 26.1% With codec enabled, it reduces to 2 GiB with a size difference of 1.8 GiB or 47.9%. (The trade-off here is the search speed performance) Collection| Old| New| Difference % ---|---|---|--- annotation| 15.8 MiB| 12.1 MiB| 23.4% area| 7.9 MiB| 4.0 MiB| 49.7% artist| 131.3 MiB| 66.2 MiB| 49.6% cdstub| 94.0 MiB| 49.8 MiB| 47.1% editor| 5.6 MiB| 1.7 MiB| 68.9% event| 2.3 MiB| 1.2 MiB| 47.5% instrument| 542.8 KiB| 370.2 KiB| 31.8% label| 10.7 MiB| 5.5 MiB| 48.5% place| 6.2 MiB| 3.2 MiB| 48.7% recording| 3.0 GiB| 1.5 GiB| 49.5% release| 37.2 MiB| 19.6 MiB| 47.2% release-group| 42.1 MiB| 20.2 MiB| 52.1% series| 1.2 MiB| 570.6 KiB| 53.9% tag| 3.6 MiB| 1.4 MiB| 61.2% url| 65.7 MiB| 38.1 MiB| 42.0% work| 363.0 MiB| 236.1 MiB| 34.9% Total| 3.8 GiB| 2.0 GiB| 47.9% Implementation details:

      Upgrading the schema:

      The Solr schema change from 1.5 to 1.7 enables docValues by default for primitive field types.
      Most primitive fields are already stored directly with stored="true". Since docValues are mainly used for faceting, sorting, and function queries, I added docValues="false" to primitive field types to avoid storing the same value twice.
      ref_count is used in boost functions in some cores , which needs docValues.
      I created a new int_dv field type with docValues="true" and used it for ref_count and the other count fields used by boost functions.
      Finally, I bumped the schema version from 1.5 to 1.7 in each core and verified that indexing and search continued to work.

      Schema upgrade PR

      Configset:

      I changed fields that can be stored directly to stored="true" and added the remaining fields with indexed="false" and stored="true".
      I then removed _store field from the schemas and request parameters.

      I replaced ngram with edge-ngram, since we don't require non-edge permutations of a query during search.

      Configset PRs

      Indexer:

      In the SearchEntity class, I added a preserve_og flag. When converting a result dictionary into a Solr document, this flag preserves the original field order instead of converting the values to a set.
      I also added an objconverter method, which allows a specific field’s structure to be converted before it is sent to Solr.
      The preserve_og flag is enabled for simple parallel fields, such as tags. These fields do not need to be stored as JSON, instead, they use two parallel multi-valued fields. Preserving their order is necessary so that each tag_count remains associated with its corresponding tag_name.
      The objconverter method is used to convert nested fields into JSON strings.

      In each Search, I moved fields from extrapaths to their respective fields and added the required objconverter methods for nested fields.
      The existing wscompat converter logic for nested objects was mirrored to create these objconverter methods.
      The remaining logic for creating MMD objects is now handled by the response writers.

      Indexer PRs

      Response Writer:

      I added MBFlatXMLWriter, a field based response writer that reads the stored fields and, by mirroring the logic in wscompat's converters, create a MMD object and write it to the response.
      I added entity-specific builders to construct MMD object from its stored fields. MBFlatXMLWriter uses these builders as helper classes.

      I made MBJSONWriter which is currently used by MusicBrainz website search, to extend MBFlatXMLWriter instead of MBXMLWriter and verified that search works.

      I also updated the tests to validate the new flat field logic instead of the old _store logic.

      Response Writer PR

      Future

      The next steps for this project are to:
      1. Benchmark it in production and measure disk, RAM, and CPU usage to see whether the optimizations hold up in a real environment or introduce any regressions.
      2. Use Solr’s default response writers instead of custom response writers, and build the MMD objects in musicbrainz-server itself.

      Conclusion

      This project was more complex than my previous one. I spent a good amount of time understanding and deep-diving into Apache Lucene and Solr internals.

      The implementation was a lot of fun because I was constantly experimenting with different approaches and reporting my findings. Each time I triggered a reindex, I stared at the terminal, hoping that indexing would not throw a wall of errors and that the core size would at least remain the same as the baseline.

      Overall, the project gave me a much better understanding of search engines, some of the distributed-system techniques used by Solr, and the complexity of MusicBrainz itself.

      I’m thankful to my mentors, @kartikohri13 and @bitmap, for their support and guidance. It has been a pleasure working with you all. Thanks for an incredible summer!

    3. 🔗 Confessions of a Code Addict Demand Paging: What Happens When Linux Handles a Page Fault rss

      Welcome back to the video series on virtual memory based on my article on the same topic. It is also available in ebook format on Gumroad (pdf/epub) and Amazon Kindle, if you prefer those formats.

      Buy PDF/Epub

      Get Kindle Edition

      So far in this video series we have covered the foundational pieces behind virtual memory:

      From here on, the next set of topics is going to be more advanced and important for debugging memory issues in real-world systems. The topic of this video is demand paging, which is central to how memory allocation works inside the kernel.

      Demand paging simply means that when you ask the kernel for more memory, it reserves a virtual address range for you but does not perform physical memory allocation until you actually need it. This can lead to tricky situations and unpredictable system performance if you do not design your systems keeping this in mind.

      The famous OOM killer that appears and kills your processes is also a result of demand paging, because the kernel may end up overcommitting more memory than what is physically available, and ultimately it has to kill a process to free up some memory to recover the system.

      In this 45-minute video, we cover in depth what demand paging is, what its benefits are, and how it works inside the Linux kernel. And if you watch the full video, you would also get an idea of how the kernel handles page faults for various situations, such as when the page has been swapped, illegal address access, accessing file-backed pages, accessing an unmapped, copy-on- write fault, etc.

      In the next follow-up video, I plan to do a hands-on demo of demand paging in action. We will use mmap and inspect the virtual memory of the process to see that merely calling mmap does not cause a physical page allocation, but accessing a page does. Wait for the next video!

      Read more

    4. 🔗 r/Harrogate Flats in Harrogate rss

      Has the flat market gone flat in Harrogate? There are a lot of flats for sale with very little movement. I think this may be because Landlords are getting rid of flats due to new rules and regulations.

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

    5. 🔗 r/Harrogate Horses on the Nidderdale Greenway rss

      After today’s walk along the Nidderdale Greenway, I’m wondering if I’m the only one bothered by the horse droppings left in the middle of the path. Does the duty to clean up after your pet apply only to dog owners?

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

    6. 🔗 Register Spill Joy & Curiosity #96 rss

      When I compare the first 10 years of my professional life as a software developer to the last two it's mind-boggling how many things that seemed foundational, or: institutional, in the life of a software developer from, say, 2008 to 2022 are now anything but that:

      GitHub and open source contributions (maintainers are closing down pull requests; who cares about their GitHub contribution graph anymore? When was the last person hired due to their open source contributions?), StackOverflow (you just said "ahh, right, StackOverflow", didn't you?), O'Reilly books, FANG as the hottest place to work, 2-week sprints (2 weeks, man! You know how long that is now?), TDD (I wrote books that use TDD! I loved TDD! And yet at some point in the last 2 years I wrote my last test by hand), text editors (man, I had heated arguments about text editors; I worked on a text editor; I was close to getting Vim tattooed on my body at some point), …

      It's like Lenin said: "There are decades where nothing happens; and there are weeks where decades happen."

      • My Laracon talk now on YouTube: How I Prompt. What an intro from Aaron. I swear I didn't pay him to say all of that.

      • I love this idea for a Greasemonkey 2.0: "Modern coding agent + browser use + a custom personal extension that allows you easily modify your most frequently visited sites. Have extension track which sites you visit and then coding agent proactively suggests modifications to make sites less distracting / more efficient." I just checked, though: 2.0 was released in 2014, so this will need to be called Jellymonkey 1.0.

      • This is the best and most beautiful and most inspiring and most thought-provoking and humbling thing I've read this week: Canon. There are many great things in this post, but here's one special snowflake of a paragraph that I want to present to you: "Cant is a language designed to be frustrating in enlightening ways. A reasonable objection might be that it will teach bad coding habits. But from my own childhood experience, what it will actually teach is why the good habits are good and how the layers of abstraction and expressibility get built one on top of another."

      • Stripe acquired OpenRouter for $7.5 billion and the letter to investors leaked. What writing! You can tell it's the Collisons: "Stripe is, of course, a private company today. We view this as a growing advantage as we venture into the vicissitudes of the singularity. The world is becoming harder to predict and we expect that deft helmsmanship will be required of every company. We're fortunate to have a corporate structure that helps us steer the right long-term course."

      • On that acquisition, I found this post by Martin Casado interesting (even though, of course, when you read his "what a time to be alive!" chant at the end and know that he invested in Cursor and OpenRouter, it's hard not to go "yeah, I bet, mate"): "AI has given us two modern miracles. The first one everyone knows, which is that we can turn electricity into intelligence. But the second one is more subtle but equally miraculous. It's that we now have intelligence as a universal medium of exchange, in the form of tokens." Tokens as an exchange medium is very interesting.

      • The Vicent Marti post on the history of scaling Git is as good as everyone says it is: Git at any scale.

      • A lot of people are shitting on GitHub for their outages but, dude, look at the numbers: 2.9 billion commits per month, 24 million new repos. "Since April, monthly commits have grown from 1.4 billion to 2.9 billion." Since April! "We have since added more than 3 million CPU cores, 120 petabytes of high-speed storage". Hot damn.

      • Thomas Dullien / Halvar Flake: Three important steps in my maturation process. The whole thing is very good and I feel bad about highlighting one of the three steps here, but, well, this bit here was very neat: "The monocausal determinism that young computer enthusiasts get used to is an illusion that generations of electrical and process engineers spent their lives perfecting and maintaining. It is because of these engineers that computer scientists could largely get away without probabilities or any empirical grounding in the past. There is an argument that you have so many natural scientists that crossed over into AI because CS education was for a long time too focused on reasoning within the deterministic monocausal illusion."

      • I need someone to explain the Nvidia & Poolside deal to me.

      • Thought-provoking, nearly sci-fi level stuff on emergency alerts: And then the men with guns tell you to do it anyway.

      • Someone tweeted a photo of a handout that David Foster Wallace gave to his students in 2002 in his Advanced Fiction class and I loved it so much that I threw the photo into Amp and had it build a nice HTML version for me that I can reference: Your Liberal-Arts $ at Work. The punctuation in dialogue is fascinating. Not only because it just seems like arbitrary rules, but also because it's completely different in German writing, or French.

      • David Senra interviewed Travis Kalanick. Listened to the whole thing and still don't know what to think of Kalanick. There's some good entrepreneur porn in there ("I can take more pain than the other guy") and Kalanick obviously loves big statements and big visions and big numbers, but I found some half-sentences here and there surprisingly nuanced and interesting. And how he thinks about "work" vs. "management capacity" matches what I think we're seeing with AI now: you still need humans, you still need to hire humans, because, yes, the AI can do the work, but you only have so much management capacity to manage that work.

      • Derek Sivers is "building my dream house without predicting - by living in a bare cabin in the woods, then adding only what I find I actually need." Here's my bet: there are at least (!) six paragraphs in there that will make you go "wait, what", each one with more incredulity than the last. But then at the end, you think: what a guy, I'm glad he's out there and doing this and writing about it.

      • People of ACM - Russ Cox. "Ousterhout introduces the term 'tactical tornado' for a programmer who churns out tons of working but overly complex code that doesn't fit well into the existing system. Worse, a bad manager often sees a tactical tornado as the most productive programmer on a team, failing to recognize the complexity and code debt left behind for the rest of the team to clean up. If we aren't careful managers, AI agents can easily become the ultimate tactical tornadoes."

      • Really, really enjoyed this Invest Like The Best episode with Ben Thompson. Listening to Thompson speculate on token prices and talk about depreciation of GPUs makes me happy.

      • Do this for me: open the Bun 1.4 release notes but before you do anything I want you to look at the scroll bar. Yes, that's a single release. I bet that whole post is AI-generated, but that's also probably the whole point of this post.

      • Same: I'm becoming AI-blind. We've had Amp post messages into our Slack when something went wrong or when something was shipped and it took just a couple of days for everyone to glaze over them. Disabled it right after someone mentioned it.

      • Very good: Responsibility Is Taken Before It Is Given.

      • Dan Luu: There's no reason for software to be slow anymore. That's right. As I was saying: if you have more bugs due to AI now, that's on you, not the AI. Dan also quotes Marc Brooker who talked about "workload-specific optimizations", i.e. optimizations of a program for a particular customer and workflow, and, man , right! AI and agents could be super compilers!

      • Gabriel Valdivia's beautiful personal timeline. Go and open this in a desktop browser, hover over the age in the top right corner, and then slide left and right.

      Hey, we're nearly there: 1 million subscribers. You could be the one that gets us over the line:

    7. 🔗 anthropics/claude-code v2.1.241 release

      What's changed

      • Bug fixes and reliability improvements
    8. 🔗 Ampcode News Friendly URLs for Sharing Orbs rss

      We use orb portals all the time to share what we're builing and get feedback.

      But the URLs were ugly and auto-generated. So, we made them nice and friendly. Now, instead of sharing t-01a0095e-9568-whatever-p1234.onamp.dev, you can share foo--yourname.onamp.dev or even use your own domain like park.mixfox.org (which is on an orb, basically).

      Sharing an orb portal in Slack with a rich preview and an Open Portal button

      This is especially nice when a portal is a long-lived application, not just a preview of changes to your local dev server. And that's increasingly how teams are using portals.

      Click the in the Portal tab to set the hostname, or ask Amp to do it for you in an orb. You can set up a personal custom domain or a workspace custom domain, or just customize the hostname prefix before --yourname.onamp.dev.

      The Portal Hostname dialog with a friendly onamp.dev hostname