05Boundaries
The core cannot grow a UI framework.
A music player for GNOME and Android
Your music library, at home and on the move. Built to feel at home on each platform.
An independent project by Marvin Baudach.
Product design, architecture and quality — with AI-assisted development.
See it in action
The desktop, the phone, and the sync between them. Recorded in the running apps.
Architecture
I separated the music library and application logic from platform code. GNOME and Android keep their native interfaces; the CLI and MCP server use the same core without a screen. Adding a platform builds on existing behaviour.
Core and edges
The boundary is enforced in the build: the core cannot depend on a UI framework. Explore CLI and MCP.
An Android album with no cover of its own gets one from MusicBrainz and the Cover Art Archive, through the same download code the desktop uses. It happens only once online sources are switched on, and the cover stays in the app's cache.
Where the lines sit
Performance
A slow title-list query led me to rebuild its database index. The measurements below show the before and after, alongside the extra storage it needs.
| What | Before | After | Delta |
|---|---|---|---|
| Title window over 100'000 tracks | 53'605 µs | 1'333 µs | −97.51 % |
| Playback ID projection | 8'125 µs | 298 µs | −96.33 % |
| Main-thread CPU while idle | 110 ms/s | 64 ms/s | −41.8 % |
| Tag reads on a warm start | 419 | 0 | −100 % |
The price sits next to it, not in the small print: the title index costs 2'379'776 extra database bytes, up 9.85 %. The track list stays pinned by test to eight cached SQL windows and 1'600 retained rows — unchanged between 10'000 and 100'000 tracks.
Quality
I use AI agents to implement focused changes, with architecture rules and automated checks governing what can merge. I also test the tests: a useful check must detect the defect it claims to prevent.
One incident · 2026-08-14
A queue test failed on header heights. The headers were fine. The fixture never installs the app stylesheet, so it was measuring widgets the app never renders.
what the test measured
Fixture, no stylesheet
what ships
The app, with its stylesheet
“A geometry assertion against unstyled widgets passes while the shipped button is a different size.”
Since then no pull request may claim Fixes #444 until three mutations turn the suite red. If one leaves it green, the claim does not go in.
Fail closed
29 checks green · ready to merge
What the checks refuse
The core cannot grow a UI framework.
It installs as a desktop app, not as a demo.
Every action works without a mouse.
A rule without a test fails the build.
Tests, lints, formatting, documented API.
The branch, the shell scripts, the worktrees.
A rule ID leads to a test, the test to a commit, and the commit to the decision. This keeps the reasoning and verification behind each change available for review.
Native design
GNOME conventions on the desktop, Material on the phone. I designed a shared visual language that preserves each platform's navigation and interaction patterns. The spectral seek bar makes the structure of a track visible on both.
On the phone, the waveform and spectrogram behind that bar arrive with the desktop sync. A track without them is decoded and analysed on the device, and the rest of the library follows in the background, only while music plays and never in battery saver.
Each platform also keeps its own habits. On the desktop the mini-player is a window of its own: switching to it hides the library window instead of resizing it, so the library keeps its size and place. On Android a long press on an artist offers Delete from device…; after a confirmation that it cannot be undone, it deletes the files of that artist's tracks from the phone.
The spectral seek bar, live
Move across the measured track to inspect its values. The bars are shaped by the same functions the apps use — bars.rs, waveform.rs and spectral_colour.rs — with only the band values standing in for live PCM.
Every bar is the RMS of its slice, mapped through the track's own p10–p95 window and smoothed against flicker. A compressed master still shows verse against chorus instead of one loud wall.
The tint is the spectral centroid: coral is low and weighty, teal high and airy.
Automation
The CLI and MCP server bring the same library into scripts and agent workflows. Capability flags control access, and changes appear in the running desktop app.
reprise-cli library summary
reprise-cli search "portishead"
reprise-cli playlist create "Focus"
reprise-cli scan ~/Music
reprise-cli instrumental create 481
reprise-cli jobs status --batch b-2f9c
reprise-cli events tail --since 0
reprise-cli concerts list --all --json
Every command takes --json for machine consumption and --db for a scratch library, so automation never has to touch the real one. Deleting a playlist refuses to run without --yes.
Tools over stdio, each behind one of seven capability flags read live from the library. A revocation takes effect on the next call. Responses never carry filesystem paths, cache locations or credentials — and the source resources omit stored URLs, because those can hold access tokens.