- ↔
- →
- August 26, 2026
-
🔗 WerWolv/ImHex Nightly Builds release
Nightly
8e67153Changelog- 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
-
- August 25, 2026
-
🔗 PrimeIntellect-ai/prime-agent Beta (v0.8.0-beta.549.1.b5ee2f8) release
Automated beta build from
main(b5ee2f81a59510e7225a0db10d65102e91e98803). -
🔗 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
/permissionsfor 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
/backgroundduring 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 agentswhile its worker was still booting (common on Windows) stopping it with "was stopped while the respawn was in flight" - Fixed
claude agentslisting 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
namealready includes the<plugin>:prefix showing it doubled in the slash menu (e.g./plugin:plugin:skill) - Fixed
claude plugin updatefailing for an installed plugin given its bare name (only the fully-qualified name worked) - Fixed plugin installation failing when
plugin.jsonwas saved with a UTF-8 byte-order mark (BOM) - Fixed
/reload-pluginsreporting 0 skills for plugins that define skills underskills/*/SKILL.md - Fixed hook error messages showing a literal
${CLAUDE_PLUGIN_ROOT}instead of the resolved plugin path - Fixed
/renamereplacing the theme's prompt border color (including a custom theme'spromptBorder) with the default cyan; the border now keeps your theme's color unless you pick one with/color - Fixed custom theme diff colors (
diffAdded/diffRemovedand their dimmed variants) being ignored in diffs and the/themepreview - Fixed a
keybindings.jsonbinding 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
/statsactivity heatmap showing each day's activity one cell off (Sunday's count under Monday) in timezones east of UTC - Fixed
/forkfrom 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/sessionsleft 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 atool_useblock without anid - 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.jsonis 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 | bashfailing 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/--resumewith a permission prompt tool, when no permission mode was set - Fixed the
Notificationhook 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-configsessions prompting to approve.mcp.jsonservers 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
apiKeyHelperreturns 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
/ultrareviewruns 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-proccasionally 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
requiresUserInteractionstill 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.jsonservers (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
maxTurnslimit now returns its output marked as partial, with a hint to continue it viaSendMessage, 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
apiKeyHelperruns at startup, and after a login token expired while idle - Changed
/code-reviewso 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 installandclaude updateto 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_hashnow reflects the plugin's real marketplace, andenabled_viaisadmin-installfor admin-installed plugins - Fixed the command sandbox's filesystem configuration not respecting
--setting-sources
- Added a startup warning for Bash allow rules with a wildcard before the subcommand (e.g.
-
🔗 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
--fastnow 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 - Install Hunk on macOS and Linux with a checksum-verified curl installer, then keep npm, Homebrew, and curl installations current with
-
🔗 @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
-
🔗 r/Harrogate St George's Dance Club rss
| 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]
---|--- -
🔗 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 -
🔗 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)
-
🔗 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.
-
- August 24, 2026
-
🔗 IDA Plugin Updates IDA Plugin Updates on 2026-08-24 rss
IDA Plugin Updates on 2026-08-24
New Releases:
Activity:
- earthbreakers
- ida-nexus
- d2f4990f: Improve analysis lifecycle
- 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
-
🔗 anthropics/claude-code v2.1.243 release
chore: Update CHANGELOG.md and feed.xml
-
🔗 r/Harrogate Is this a real road between Markington and Ripley? Can I cycle it on a road bike? rss
| 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]
---|--- -
🔗 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.
-
🔗 r/Harrogate Sewage smell in Bilton area?? rss
Absolutely honking around here, anyone know what's going on?
submitted by /u/Trancer79
[link] [comments] -
🔗 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_tagtable inadmin/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_tagColumn| 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 playlistOn 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/collectionslists the logged-in user’s collections./collection/<mbid>/shows the collection page which follows the same SPA pattern as other ListenBrainz pages .GETreturns the HTML shell, andPOSTreturns 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 withuseInfiniteQueryinstead 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=trackson 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.tsxfor playlist search. - Integration tests in
test_playlist_api.pyfor tags. - Frontend tests in
Playlist.test.tsxfor in-playlist sorting. - Integration tests in
test_musicbrainz_collections_import.pyfor 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
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. -
🔗 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
GoogleThinkingLeveltype toGoogleApiThinkingLeveland addedResolvedGoogleThinkingLevelfor normalized adapter levels.
Added
- Added an optional
powershelltool for Windows, configurable throughdefaultToolsand the SDK. See PowerShell Tool. - Added a
/thinkingselector 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_failedextension events so compaction failures and aborts expose their reason, retry state, source, and error message to handlers (#8175). - Added inherited provider-neutral
toolChoicesupport 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-0813support to the Qwen Token Plan Individual catalog (#8194).
Changed
- Changed experimental installer-managed installations so
pi updatestages, 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-Agentunless 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.jsontypings omitting the documented OpenAI-compatiblecompat.supportsFinishReasonprovider and model override (#8487 by @petrroll). - Fixed
/modeland/thinkingselections being persisted globally unless explicitly saved with Ctrl+S (#5263). - Fixed JSON and RPC
toolcall_startevents 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
edittool 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.mdandAGENTS.mdin 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_detailsverbatim 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
toolChoicein 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_tokenscount 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.jsonandmodels-store.jsonoverriding 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 updatefrom downgrading already-newer installed packages (#8226). - Fixed built-in llama.cpp models disappearing from
/modelwhen/llamarefreshed a configured server underPI_OFFLINE, and included idle-sleptsleepingrouter 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
/llamabefore/modelwhen 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
/modeland--list-models(#8187). - Fixed branch summary entries recording the navigation destination in
fromIdinstead 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).
-
🔗 Barre/ZeroFS v2.3.1 release
-
🔗 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] -
🔗 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) -
🔗 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 thinLoginterface:// 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 becauseLogis acompanion objectcalled 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
androidMaineven though most of it was plain Kotlin.The interface stays, the backend changes
The first decision was to keep the
Loginterface and its companion. It's called from everywhere, changing the call sites would have made the diff unreadable and the review impossible. SoLogmoved toshared/util/Log.kt, kept its shape, gainedi/v/logand athrowableparameter, and started delegating to a KermitLoggerpulled 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
SharedFileLogWriteris an abstract class incommonMainthat extends Kermit's LogWriter and does everything except the write. Click to see the implementationabstract 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
Channelis 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 singleDispatchers.IOcoroutine 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. SincecommonMaincannot seeBuildConfig, so anything that wants build metadata takes aBuildInfoinstead.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() }AndroidLogSubmitteris one of the extension ofLogSubmitterin androidMain which zips the.txtfiles, exposes the archive through FileProvider, and fires anACTION_SENDchooser. Two review-driven fixes landed here: -- Passed the
PlatformContextasapplicationContextthrough the parameters of the class - Handled thread switching explicitly, file compression runs on
Dispatchers.IO, while building and launching the chooser intent switches back towithContext(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.ktand aSharedAppModule.ios.ktfor 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 incommonMainunderplatformModuleand make only the factoriesexpect/actuallike 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.ktis 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.orgthat pushes two events,listenandplaying_now. The existing implementation usedio.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.ioandSocket.ioprotocol 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 implementationclass 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+awaitCloseshape survived the rewrite unchanged, which is the nice thing about having had a decent boundary in the first place,SocketRepository.listen()returns aFlow<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) andlifecycle-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
appmodule 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 funof the engine with its platform-specificactual funimplementation. 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<*> = DarwinThe 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
commonMainhas noR. I could have movedres/, 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
ListensRepositoryneedsgetPackageLabel(pkgName), which turnscom.spotify.musicinto "Spotify". That's aPackageManagercall 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
AndroidListensRepositoryImpland anIosListensRepositoryImplsharing 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 behindexpect. The PR consists the moving of four entities, four DAOs,TypeConverterandTransformeracross, 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
ContextandgetDatabasePath().absolutePath. iOS supplies a path fromNSFileManager. One line of diff , each platform.BundledSQLiteDriver()in common means both platforms ship the same SQLite built. This is the same "one module incommon, only the factories areexpect/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
@Suppressisn't style. KSP generates theactual, 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, andcommonMainhas noR:// 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
StringProviderandDrawableProvider, 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, soMIGRATION_2_3rebuilds 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 repairssqlite_sequencesoAUTOINCREMENTdoesn't restart into ids that still exist.
Click to see the SQLite codeINSERT 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 becauseNotificationListenerServicehas 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
BackgroundTaskSchedulerinterface withWorkManageron Android andBGTaskScheduleron 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 androidxData: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
ListenSubmissionStatemoved 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.ListenSubmissionServicein 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
PermissionEnumthat 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
imageaDrawableResourceenum resolved by theDrawableProvider, so my shared code can name an icon without knowing what anR.drawableis. That left me one problem.SEND_NOTIFICATIONSlives in common but still needs an Android manifest string and aminSdkon Android.
I solved it with an extension inandroidMainthat supplies the Android facts for shared constantsThe 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.commonMaincannot nameActivity, and Android's rationale APIs (shouldShowRequestPermissionRationale) genuinely require one. So my Android implementation casts (activity as? Activity) and degrades gracefully to aFLAG_ACTIVITY_NEW_TASKintent 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_NOTIFICATIONSsince 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 explicitidfield, and that's the only reason I putidon 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:
- iOS file logging and log submission are
TODO(). The shared machinery (queueing, formatting, startup metadata, theLogSubmittercontract) is all done and platform-neutral, the two iOS leaves are stubs. - System bar control away from
accompanist-systemuicontroller. - Browser and external URL handling via
expect/actual. - Lottie to Compottie.
- Shimmer to a KMP shimmer implementation.
- Onboarding screen migration and all other composable screens migration to CMP, started but not complete.
- 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.
-
🔗 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.
-
- August 23, 2026
-
🔗 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…
-
🔗 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 withstored="true". Since docValues are mainly used for faceting, sorting, and function queries, I addeddocValues="false"to primitive field types to avoid storing the same value twice.
ref_countis used in boost functions in some cores , which needs docValues.
I created a newint_dvfield type withdocValues="true"and used it forref_countand 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.Configset:
I changed fields that can be stored directly to
stored="true"and added the remaining fields withindexed="false"andstored="true".
I then removed_storefield from the schemas and request parameters.I replaced
ngramwithedge-ngram, since we don't require non-edge permutations of a query during search.Indexer:
In the
SearchEntityclass, I added apreserve_ogflag. 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 anobjconvertermethod, which allows a specific field’s structure to be converted before it is sent to Solr.
Thepreserve_ogflag 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 eachtag_countremains associated with its correspondingtag_name.
Theobjconvertermethod is used to convert nested fields into JSON strings.In each Search
, I moved fields from extrapathsto their respectivefieldsand added the requiredobjconvertermethods for nested fields.
The existing wscompat converter logic for nested objects was mirrored to create theseobjconvertermethods.
The remaining logic for creating MMD objects is now handled by the response writers.Response Writer:
I added
MBFlatXMLWriter, a field based response writer that reads the stored fields and, by mirroring the logic inwscompat'sconverters, create a MMD object and write it to the response.
I added entity-specific builders to construct MMD object from its stored fields.MBFlatXMLWriteruses these builders as helper classes.I made
MBJSONWriterwhich is currently used by MusicBrainz website search, to extendMBFlatXMLWriterinstead ofMBXMLWriterand verified that search works.I also updated the tests to validate the new flat field logic instead of the old
_storelogic.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!
-
🔗 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.
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!
-
🔗 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] -
🔗 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] -
🔗 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:
-
-
🔗 anthropics/claude-code v2.1.241 release
What's changed
- Bug fixes and reliability improvements
-
🔗 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 sharefoo--yourname.onamp.devor even use your own domain like park.mixfox.org (which is on an orb, basically).
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.
-