Repository navigation
Releases: muen-collective/mitsumeru-app
Release list
Mitsumeru 0.2.8-dev
Mitsumeru 0.2.8-dev
Internal build — carries the -dev suffix; the update channel is dev.
Wraps DeepSeek Harness 0.2.1-rc.2, unchanged. This cut is the updater's: one loop fixed, and
the regression test that would have caught it.
What changed
-
A scheduled check can no longer move you backwards (founder, 2026-10-23: "i updated and
restart but i did again so I think theres a bug somewhere"). A build carrying an unpublished
version sat ahead of the channel, so every check found the older published version, and because
allowDowngradewastruefor every check, electron-updater offered it,autoDownloadfetched
it, and the banner offered a restart that could only ever go back. The version string never moved
forward, so the next check offered it again — a loop with no exit, driven by a timer.allowDowngradenow follows the trigger: a person asking (the pill, the menu's Check for
Updates) may roll back; a scheduled check, a resume check and the startup check may not. Rollback
is a decision, not a side effect. -
A downloaded version equal to the running one is never offered. electron-updater already
filters equal versions itself, so this is the last word rather than the first defence — but it
costs one comparison and closes the case where a cached download and the running bundle disagree. -
The updater smoke gained case C: a stand-in feed serving a genuinely older version, asserting
that a scheduled check neither offers it nor downloads it. Mutation-tested — restoring
allowDowngrade = truefails the case with "a scheduled check OFFERED the older 0.2.8-alpha.1",
which is exactly the loop that was reported.
A note on the fixture, because it bit
The first version of case C used 0.2.8-older as the "older" version. Pre-release tags sort
alphabetically in semver, so older > dev and the app correctly offered it as an update —
the test was wrong, not the fix. It now uses -alpha.1.
What to check
- Leave the app running past its startup check. The pill should stay invisible, and the log should
readupdate-current version=0.2.8-dev. - Nothing else moved: the folder chooser from 0.2.7-dev, the update pill, the account row and the
collapsed rail are as that release left them.
Mitsumeru 0.2.7-dev
Mitsumeru 0.2.7-dev
Internal build — carries the -dev suffix; the update channel is dev.
Wraps DeepSeek Harness 0.2.1-rc.2, unchanged from 0.2.6-dev. This cut is the shell catching up:
one behaviour fix, no harness change.
What changed
-
A new workspace folder can be named (founder, 2026-10-22: "I can open finder but when I try
to create new folder, it can't give it a name"). The sidebar's new-project flow opened the
harness's own macOS picker, which is anNSOpenPanelrun by a separateosascript
accessory process. That panel is never the app window's key window, so New Folder created a
folder but its rename field refused every keystroke — the name had to be accepted as
untitled folderand renamed afterwards in Finder.The shell now exposes
__DSH_DIRECTORY_PICKER__— the same global DSH's own desktop exposes —
and answers it with an Electron sheet attached to the main window (openDirectory+
createDirectory). Naming a folder is ordinary text entry again: type, press Return, choose.
The harness client prefers that bridge whenever it exists, so nothing in the pinned harness
changed and no upstream patch is carried.Verified on a live run before the cut: the dialog's owner is the app, no
osascriptprocess is
involved, the sheet carries New Folder / Cancel / Choose, and a pick completed with a newly
created folder. The founder then drove the same dialog by hand and confirmed the name can be
typed.
What to check
- Sidebar → new workspace → New Folder → type a name → Return → Choose. The folder should be
created with the name you typed and selected as the workspace. - Nothing else moved: the updater pill, the account row and the collapsed rail are as 0.2.6-dev
left them.
Mitsumeru 0.2.6-dev
Mitsumeru 0.2.6-dev
Internal build — carries the -dev suffix; the update channel is dev.
These binaries replace the ones published earlier today. The first cut of
0.2.6-devshipped an
updater guard that could not work: it remembered the installed version in memory, and
quitAndInstall()ends the process. Installing that build and restarting would have re-offered the
same update — the exact bug the guard was written for. The assets under this tag were rebuilt and
replaced at ~11:20 UTC on 2026-10-20; GitHub reports only the publisher's own verification downloads
against the earlier ones. If you downloaded before then, download again — the version string is
unchanged, so nothing would ever offer you the fix.
Wraps DeepSeek Harness 0.2.1-rc.2 (0.2.5 carried 0.2.1-alpha.2; rc.2 shipped 2026-10-17). The
pin is the only thing that had to change to wrap it — the harness version is data, not code. An rc pin
never ships to latest, so this build stays on the dev channel deliberately.
What changed
- DSH desktop parity — the rail now collapses all the way to zero (founder, 2026-10-12:
"i want full parity", after "dsh left rail collapse all the way, this is the design I always
wanted"). The shell now marks the document<html data-platform=darwin>, which is the single
attribute the harness reads to switch three behaviours at once:- the collapsed sidebar's width becomes
darwin ? 0 : 56, so a closed rail leaves nothing on
the left instead of a 56px icon column (ui-layout); - the desktop chrome turns on —
[data-window-drag]regions, the translucent New-session button,
the sidebar gradient; - the native keyboard bridge becomes mandatory: with the mark set,
dsh-client-shortcuts
throwsDesktop keyboard bridge unavailableunlesswindow.dshDesktop.keyboardexists, and the
frame does not mount at all.
- the collapsed sidebar's width becomes
- The native keyboard bridge is real, not a stub. In desktop runtime the DOM key path serves only
fixed actions, so the configurable bindings must arrive over IPC. The main process imports the
harness's own published domain module (@deepseek-ai/dsh-client-shortcuts/protocol) to resolve the
binding table, claims registered chords onbefore-input-event, and delivers them to the page.
Preferences persist at<stateDir>/keybindings.json. - Window chrome:
titleBarStyle: hiddenInsetwithtrafficLightPosition {x:16, y:18},
vibrancy: 'sidebar'and a transparent background — the official shell's own darwin values. The
vibrancy matters: under the mark the harness's frame isbackground: 0 0and its sidebar is a
color-mix(…, transparent)gradient, so a native material has to sit behind it. - Safety: the preload marks the document only when the bridge is confirmed live, so if the
protocol module cannot load the app degrades to the previous behaviour (56px rail, working app)
rather than a dead boot. The previous session'srail-collapse.tsinjection stays as the fallback.
Fixed after the first parity build (same day, founder's eyes)
- The colour was off. The native material behind the sidebar followed the OS appearance while the
app ran its own theme, so a light app on a dark system translated a dark grey panel behind a light
UI. The shell now mirrors the harness's theme source (html[data-ds-theme-source],system|
light|dark, written byui-layout) into Electron'snativeTheme.themeSource, which fixes the
material, the window chrome and rendererprefers-color-schemein one step. Fullscreen is mirrored
the same way, so the harness's 48px title-band clearance and the hidden traffic lights agree. - "If I click settings, another menu pops up top left." Our Muen account overlay (Epic 92) hid the
stock settings rail and then clicked the hidden launcher button. A hidden element measures0x0,
so anything anchored to it opens clamped at the window's top-left. Root cause, read from the
harness's own source: thesettings.launcherslot is filled byAccountMenu— that launcher is
the account menu's anchor, which is why the stray panel was the account menu (Settings / Feedback /
Sign in). The overlay now opens Settings through the harness's command path instead —
{kind:'menu', commandId:'settings.open'}over the bridge, the same mechanism the official shell
drives its File menu with — which involves no DOM anchor and no key gesture at all.
Fixed at the founder's review (same day, later)
- The avatar's Language menu listed two languages, not the catalog (founder, 2026-10-12: "the
language switcher from avatar only show zh, en, needs full set from language switcher"). The menu
carried a hardcodeden/zhpair whose comment said it was the harness's shipped catalog — true,
and exactly why it was wrong: the other five languages arrive from@muen/dsh-localizethrough
the runtime'saddLanguage, and this menu was the one place the catalog never reached. It now reads
<html data-muen-languages="[{id,label}]">— the mirror that plugin publishes, in the runtime's own
order — and falls back to the built-in pair when nothing published one (a stock DSH, or a profile
without the plugin). The rows are rebuilt on open, not at inject time, because the overlay is
injected as the app boots, before any plugin has offered its languages.
The update strip is gone (2026-10-20)
- The banner across the top of the window became a pill beside the account row. A full-width strip
took permanent screen space to say something true for about a minute, and it sat in the window chrome
rather than the account area where app-level actions live. It is replaced by what DSH Desktop does: a
small button — 14px glyph + label — injected into the avatar row, drawn only while there is something
to say. The glyph carries the phase (download arrow when available, spinner while checking /
downloading / verifying, check when downloaded, dot on error) and the click does what the phase
implies: Restart when downloaded, Retry on error, Release notes when available, nothing
while busy. The visibility rules carry over from the banner unchanged —checkingandcurrent
only appear when a person asked,erroronly when they asked or when an update was already in
flight — because that reasoning was about not interrupting someone, not about where the thing is
drawn.update-banner.tsis deleted;update-indicator.tsreplaces it. - A 100% download is not "done". electron-updater still verifies and unpacks after the last byte,
and the old progress bar sat frozen at 100% through that gap.verifyingis a state now, so the pill
says what is happening instead of looking stuck. - A version already handed to
quitAndInstallis not offered again on the next check (founder,
2026-10-19: "when I restarted it did the update banner again"). After the relaunch the on-disk
version may not have changed yet, so the same version came back as available. The updater now
remembers what it asked Squirrel to install and reportscurrentinstead — and the marker lives on
disk (<userData>/update-installed.json), becausequitAndInstall()quits the process: the
first cut of this guard was an in-memory value that could not survive the relaunch it existed for.
It is bounded so a failed install still re-offers: 15-minute TTL, consumed on first use, and dropped
at startup when the running version is already the marked one. - The account row spans the sidebar instead of floating as a pill: it reads the sidebar column's
live box, follows a resize, reserves its own height at the foot of the rail so content scrolls above
it, and hides when the rail collapses to zero — it lives inside the rail, so it goes with it. It is
transparent, not a token fill: on macOS the sidebar paints a gradient plus acolor-mixover
--dsw-specific-sidebar-fill, so any flat copy of that token leaves a visible seam along the row's
top edge. The row is a<button>, so the UA's outset border had to be reset — that border was the
white line it wore. - No leading seat was built, deliberately. DSH's sidebar toggle and New Session buttons are its own
HeaderLeadingControls, mounted byAppFramewhendarwin && sidebarCollapsed. This shell already
setsdata-platform, so they were mounting all along; an injected copy only stacked a second pair on
top of them. The native pair keeps its own icons, tooltips and React handlers.
Gates
This cut was made with pnpm release:cut, which stops at the first failure and never publishes. Every
step below ran, in order, on the artifacts that are attached to this release:
check:label,check:identity,typecheck— clean.package:mac— both arches built and signed with Developer ID Application: THUY QUANG PHAM
(4Q6GC57QG4), each carrying a secure timestamp.notarize:all— 8 submissions accepted by Apple's notary service (app bundle + final dmg, per
arch), each stapled, and everystaple and validatereported worked. Gatekeeper accepts them.smoke— 5/5: harness readiness line, UI loaded, clean quit, no orphan harness, app exited.smoke:native— the native surfaces against the packaged app.verify:surfaces,verify:mount,verify:release— all green, including the mount probe reading the
served bundle and the frame's own seats.verify:artifacts— 20/20: each dmg/zip carries its own arch's addon and not the other's, and the
Windows NSIS payload is x86-64 with the win32-x64 addon and every shipped@muenplugin.verify:docs— "docs agree with the code".
Two gates failed first and were fixed rather than waived, because both were the gate's fault and not the
build's:
smokeblocked on a Keychain prompt. It shareduserDatawith the installed app, so it read that
app's stored Muen session an...
Mitsumeru 0.2.5
Mitsumeru 0.2.5
Promoted release: no -dev suffix, so this takes the latest channel. Shipped three targets:
Apple Silicon, Intel and Windows x64.
This is the build that was dogfooded as 0.2.5-dev, promoted under its own name. The -dev
drills (0.2.4-dev, 0.2.5-dev) stay published and keep their own channel, so nothing already
installed is disturbed.
Wraps DeepSeek Harness 0.2.1-alpha.2, from 0.1.7-rc.1, which is what 0.2.3 shipped.
What changed since 0.2.3
Sign-in is required. A fresh install opens at the sign-in screen. There is no anonymous use,
the harness does not start until a session exists, and signing out stops it and returns to the
gate. Sign-in opens the Muen site's sign-in route in your browser; the mitsumeru:// callback
returns into the app, where avatar and tier read the same membership record the site sees. The
entry also registers the mitsumeru:// protocol pair, so links from the site round trip back
into the app.
The Plugins page shows what you actually have. It renders the shipped Muen bundles (they were
invisible before: the profile listed them and the page filtered them out), the account row shows
your account name, and Settings now lives in the avatar menu (the old gear rail is hidden;
Cmd+, still works). Every @muen/* plugin card wears the same Experimental badge DSH gives its
own preview plugins.
Retired inside the build: dsh-turn-summary and dsh-codex-fold left the shipped set. What
remains vendored is brand, EVA theme, white-label, context-watchdog and changes-card.
Avatar row polish: the light-theme label is fixed and the avatar ring is gone.
The harness re-pin. 0.1.7-rc.1 to 0.2.1-alpha.2. The supply-chain age gate holds the harness
closure as an explicit, version-reviewed exception list, and a re-pin must retarget it or the build
refuses to run: 289 entries were retargeted here. Every re-pin is audited against the three
upstream breakage classes (settings and service surfaces, composition gates, profile bundles).
What is not in this build
The Generate, Assets and localisation plugins are not here. They are separate packages that
ship from the market: install them in Settings to Plugins by URL. This build is the shell, the
harness and the brand. Private-plugin gating and pricing are also not here; they ship with the
Muen plugins installer in the 0.2.6 line.
Gates
Run by pnpm release:cut, which stops at the first failure and never publishes:
PROMOTE=1 check:label(the-devsuffix must be gone from the version and from every artifact)check:identity,typecheck- build and sign all three targets, then
notarize:allandmanifest:merge smoke,smoke:native,verify:surfaces,verify:mountverify:release,verify:artifacts,verify:docs
Install
| Artifact | Architecture |
|---|---|
Mitsumeru-0.2.5-arm64.dmg / -arm64-mac.zip |
Apple Silicon |
Mitsumeru-0.2.5.dmg / -mac.zip |
Intel (x86_64) |
Mitsumeru Setup 0.2.5.exe |
Windows x64 |
- Apple Silicon:
Mitsumeru-0.2.5-arm64.dmg - Intel:
Mitsumeru-0.2.5.dmg - Windows: the
.exeinstaller. It is unsigned, so Windows shows SmartScreen: More info
then Run anyway.
Both Mac builds are notarized and stapled, so they open on a double-click. latest-mac.yml and
latest.yml are the update feeds; latest-mac.yml is the one a promoted client reads, so a
0.2.3 install is offered this build automatically.
Mitsumeru 0.2.5-dev
Internal build — carries the -dev suffix; the update channel it belongs to is
dev.
The same app, on a new harness, with sign-in. This build re-pins DeepSeek
Harness 0.1.7-rc.1 → 0.2.1-alpha.2 (the 0.2.0-rc.2 → alpha.1 → alpha.2
re-pin chain of 2026-09-29…10-09; an alpha pin never ships to the latest
channel — 0.2.6 waits for the 0.2.x rc).
Sign-in is now required. The first-install gate (founder direction
2026-10-02): a fresh install opens at the sign-in screen — no anonymous use,
the harness does not start until a session exists, and explicit sign-out stops
it and returns to the gate. Sign-in opens the deployed Muen site's sign-in
route in your browser; the mitsumeru:// callback returns into the app, where
avatar and tier read from the same membership record the site sees. The entry
also registers the mitsumeru:// protocol pair, so links from the site round
trip back into the app.
Plugins page shows what you actually have. The page renders the shipped
Muen bundles (they were invisible before — the profile listed them but the page
filtered them out), the account row shows your account's name, and Settings now
lives in the avatar menu (the old gear rail is hidden; ⌘, still works). Every
@muen/* plugin card wears the same Experimental badge DSH gives its own
preview plugins — one patched predicate in the plugin-manager client, inside
the app signature, with a build gate so a future harness bump that changes the
line fails the cut instead of shipping unbadged.
Retired inside the build: dsh-turn-summary and dsh-codex-fold left the
shipped set (founder, 2026-10-02). What remains vendored: brand, EVA theme,
white-label, context-watchdog, changes-card.
Avatar row polish: light-theme label fixed, no avatar ring.
The bump's own findings. The supply-chain age gate holds the harness
closure as an explicit, version-reviewed exception list (289 entries retargeted
to 0.2.1-alpha.2); a re-pin must retarget that list or the build refuses to
run. Every bump is audited against the three upstream breakage classes
(settings/service surface, composition gates, profile bundles).
Unchanged: private-plugin gating and pricing are not here — the Muen
plugins installer ships later (0.2.6 line). Windows ships unsigned, so first
run needs one More info → Run anyway click.
Wraps DeepSeek Harness 0.2.1-alpha.2 (from 0.1.7-rc.1).
Packaged for all three targets — Mitsumeru-0.2.5-dev-arm64.dmg +
-arm64-mac.zip (Apple Silicon), Mitsumeru-0.2.5-dev.dmg + -mac.zip
(Intel) and Mitsumeru Setup 0.2.5-dev.exe (Windows x64). Both Mac builds are
notarized and stapled; the feed is the merged dev-mac.yml, with dev.yml
for Windows.
Install: Mitsumeru-0.2.5-dev-arm64.dmg (Apple Silicon),
Mitsumeru-0.2.5-dev.dmg (Intel), or Mitsumeru Setup 0.2.5-dev.exe
(Windows x64), from this release's assets. A 0.2.4-dev machine is offered this
build by the app itself through the update banner.
Mitsumeru 0.2.4-dev
Internal build — carries the -dev suffix; the update channel it belongs to is
dev.
The app now announces its own updates. Until now a release reached you
because a person said so — the shell checked, downloaded and waited, but said
nothing, and an update could sit unseen until someone mentioned it. A 36 px
shell-owned strip at the top of the window surfaces that state: a check you
trigger flashes "up to date" for three seconds, an available update offers
Download, progress runs against a named version, and a finished download offers
Restart to update. A failure shows when you asked for a check, or when an update
already in flight died, and stays quiet otherwise (10 s); dismiss hides the
strip until the state next changes. It has to be shell-owned — quitAndInstall()
is an Electron API, and a harness plugin cannot drive any of this.
Running the five states against the packaged app with a local stand-in feed
caught two bugs, both fixed here: an error after a failed restart hid itself
(the strip still believed the state was downloaded), and the progress line
read "Downloading v… 22%" because download-progress events carry no version of
their own.
Muen sign-in, in the slot DeepSeek Desktop uses. The lower-left avatar is
that same seat: signed out it shows the Muen mark, and a click starts sign-in —
GitHub OAuth in the browser, a mitsumeru://auth/callback?code=… deep link back
into the app, and a code exchange the shell performs in the main process. The
session lands encrypted (safeStorage) at <userData>/auth/session.enc; the
token never leaves the main process, and the injected overlay only ever sees the
profile. Signed in, the avatar shows your picture (your initial if the image
fails to load), with a PRO badge when the session says membership: pro.
The deep link needed a protocols entry in the build config — without it macOS
hands the URL to nobody and sign-in can never complete.
The avatar opens a menu, and the language switcher moved into it. The row
opens on click, the DeepSeek Desktop pattern: Language (English / 中文),
separator, Sign out. The language item writes the harness's own locale
preference over the same wire Settings → General uses, so the two surfaces
cannot disagree — and the client applies it live, html lang flipping in about
2.5 s with no reload. The overlay takes the new language on the click itself, so
its own bilingual labels do not trail the app; the menu closes on an outside
click and on Esc, and a failed switch shows an inline error instead of ignoring
the click.
What is deliberately not in this build. Sign-in was verified against a local
mock auth server; the production service is not standing yet, and the build
points at mitsumeru.vercel.app (MITSUMERU_AUTH_ORIGIN moves it for a run).
Private-plugin gating — the part that reads membership and unlocks the Muen set
— is not here either.
Wraps DeepSeek Harness 0.1.7-rc.1 — unchanged. The re-pin to 0.2.0-rc.2 follows
as its own release, so a shell change and a harness change can be told apart if
something misbehaves.
Packaged for both Mac targets — Mitsumeru-0.2.4-dev-arm64.dmg +
-arm64-mac.zip (Apple Silicon) and Mitsumeru-0.2.4-dev.dmg + -mac.zip
(Intel) — signed with a Developer ID, notarized and stapled (every artifact
carries its ticket), with the merged dev-mac.yml feed. The Windows installer
follows with the production cut. Gates: check:label / check:identity /
typecheck; pnpm smoke (5/5);
pnpm smoke:updater (14/14, against the packaged build); the five banner
states, sign-in and sign-out, session restore across a SIGKILL and relaunch,
and the language switch — all against the packaged app, all screenshot-asserted
(artifacts/current-*, error-*, downloaded-*, auth-*, menu-*).
Install: Mitsumeru-0.2.4-dev-arm64.dmg (Apple Silicon) or
Mitsumeru-0.2.4-dev.dmg (Intel), from this release's assets. A machine on the
dev channel is offered it by the app itself. From this build on the app
announces its own updates — this is the last release that had to be relayed by
hand.
Mitsumeru 0.2.3
Production build — latest channel, like 0.2.0, 0.2.1 and 0.2.2. This release ships three
targets: Apple Silicon, Intel, and Windows x64.
The Generate, Assets and localisation plugins are NOT in this build. They are separate packages that ship
from the market, by the founder's decision of 2026-09-26 — install them in Settings → Plugins by URL
(@muen/dsh-generate 0.2.2, @muen/dsh-assets 0.2.1, and optionally @muen/dsh-localize 0.1.0). This build is
the shell, the harness re-pin and the brand.
The brand now travels inside the build. Until now a white-label brand lived
only in runtime state, so installing a new core looked exactly like losing it:
the tagline, the status label, the accent and the marks were gone, and re-adding
them by hand did not survive the next launch either. The cause was an upstream
API change, not the plugin's logic.
DSH 0.1.6-alpha.2 exposed settings.register(namespace, schema, { base }), and
@muen/dsh-white-label used it. 0.1.7-rc.1 removed it — the settings service
now exposes configure, describe, update, replace and mutate, and
nothing else. The call threw register is not a function into the catch that
had been there to tolerate a namespace someone else had already taken, so it
failed silently: nothing logged, the namespace never resolved, and persistence
fell back to the browser's localStorage mirror. That mirror is not durable —
the harness serves a fresh port on every launch and localStorage is
origin-scoped — so the brand was wiped by the next start.
0.1.7 also replaced that API with a better one, and the plugin now uses it: a
plugin's durable namespace is its own profile entry id, and its Config is the
namespace, projected to the browser when it carries volatile() fields. So the
brand is not copied into a state directory and it is not baked into a client
bundle. It is config in the layer that belongs to the build — the plugin's
own bundle patch — and the patch layer order does the rest:
| Layer | Holds | Outranks |
|---|---|---|
| the plugin's bundle patch | the brand this build ships | — |
the profile's cordis.patch.yml |
what a person saved in Settings → Brand | the build's brand |
$DSH_HOME/cordis.patch.yml |
an operator's machine-wide override | both |
The consequence is the one that matters for release: a brand someone saves
survives an app update, and Reset restores the shipped values.
Ship or change the brand from one file. build/brand/white-label/brand.json
plus the PNGs beside it are the whole input, and scripts/apply-white-label-brand.mjs
folds them into the vendored plugin's patch during the build (prepare-harness.sh
runs it after vendoring, so it always writes onto a fresh copy). Delete that
folder and the build ships the plugin's neutral defaults — no build carries an
identity it was not given. This build ships the authored brand: the tagline, the
run-status label, the light accent, and the light/dark wordmark pair.
The accent is durable too. It had the same failure mode, and it rides the
same document now — keeping it in localStorage alone would have reproduced the
loss for anyone who picked a colour.
A duplicate Brand page is suppressed. A schema with volatile() fields
auto-generates a Settings page, and auto defaults to true, so declaring the
Config would have put the brand on a second, generated page as well as the
plugin's own. The plugin registers configure({ auto: false }, ctx.fiber) as an
effect, per the dsh-settings README rule for a plugin that ships its own page.
A new harness underneath: DeepSeek Harness 0.1.7-rc.1, up from
0.1.6-alpha.2.
The Sidebar Browser tab is off by default, and that is upstream's choice.
0.1.6-alpha.2 composed ui-sidebar-browser unconditionally. 0.1.7-rc.1 keeps the
package — @deepseek-ai/dsh-client-ui-sidebar-browser, "Sandboxed Web browser
tabs for the right Sidebar" — but gates the row on the profile being named
desktop:
# Web profiles opt in; Desktop retains sandboxed HTTP(S) Browser tabs.
- id: ui-sidebar-browser
name: '@deepseek-ai/dsh-client-ui-sidebar-browser'
disabled: !!js "ctx.get('profileContext')?.name !== 'desktop'"This app's profile is named mitsu, so the gate never passes and the tab
vanishes — it is not removed, and nothing in this repo disabled it. To opt back
in, override the row by id in the profile's cordis.patch.yml:
- id: ui-sidebar-browser
disabled: falseThat is verified in a throwaway profile: the row composes with disabled: false
and the plugin reaches the DOM. One thing to know before enabling it: dsh-base
gates desktopPlatform (the DeepSeek account's desktop handoff) on the SAME
profile name, but wraps it in a fallback, so a browser-only override is safe —
while renaming the profile to desktop would also switch the account plugin
into its desktop mode, which is a different decision than re-enabling a tab.
The override-by-id route changes exactly one thing.
Verified on this build
node --test tests/*.test.mjsinmuen-plugins— 47 pass, including three new
cases pinning this contract: the namespace is the entry id and not the old
name, the 0.1.6registercall is capability-guarded rather than left to
throw, and the accent writes through to the durable document.--dump-configon a throwaway profile with an empty profile patch: the
brand arrives from the bundle layer (brandTagline,statusLabel,accentLight,
and both logos), and a row added to the profile layer replaces it.- The packaged app, in a fresh profile with empty
localStorageand an empty
profile patch: Settings → Brand reads back the shipped tagline and status label,
and all seven brand marks render. - The shipped accent is live, read off the running app rather than the schema:
--dsw-alias-brand-primary: #4f83f2and its derived
--dsw-alias-button-primary-hover: #6f99f4. scripts/white-label-probe.cjsagainst the packaged harness: PASS.- Both architectures are notarized and stapled, and the full release gate passes
for each:Mitsumeru-0.2.3.dmg+-mac.zip(Intel) and
Mitsumeru-0.2.3-arm64.dmg+-arm64-mac.zip(Apple Silicon). Gatekeeper
accepts the dmg, the zip, and the app inside each; every Mach-O binary in the
harness tree is signed;verify:archboots both and confirms neither carries
the other's native addon.
Merging the feed for the first time caught a second bug, in manifest:merge: it
concatenated whatever fragments were in release/, so this build's
dev-mac.yml claimed version: 0.2.3-dev while listing the 0.2.2 Intel zip
and dmg — which an Intel client would have downloaded and installed as
"0.2.3-dev". Fragments from another version are now dropped, loudly, and a feed
with no entry for the current version fails instead of shipping. The same merge
is what makes a one-arch release visible: it warns when fewer than two artifacts
are present rather than quietly publishing a feed that strands a platform.
Fixing the accent check turned up a flaw in the probe itself: it read the token
from documentElement, while the plugin's theme override lands on <body>, so it
reported "default" for a build whose accent was applied. It reads <body> now —
the accent is the one part of the brand a screenshot cannot confirm, and a false
negative on it is exactly the kind of result that sends you re-checking a correct
change.
Known, and deliberate
A hand-written row in the profile's cordis.patch.yml replaces the whole
config object instead of merging field by field, so a row that sets only
brandTagline drops the shipped marks. The Settings UI is unaffected — it writes
the full resolved config, and Reset deletes the row and brings the build's values
back.
Install
| Artifact | Architecture |
|---|---|
Mitsumeru-0.2.3-arm64.dmg / -arm64-mac.zip |
Apple Silicon |
Mitsumeru-0.2.3.dmg / -mac.zip |
Intel (x86_64) |
Mitsumeru Setup 0.2.3.exe |
Windows x64 |
- Apple Silicon:
Mitsumeru-0.2.3-arm64.dmg - Intel:
Mitsumeru-0.2.3.dmg - Windows: the
.exeinstaller (unsigned — Windows shows SmartScreen, so More info → Run anyway)
macOS builds are notarized and stapled, so they open on a double-click. latest-mac.yml and latest.yml are the
update manifests and ship beside the binaries.
Windows needs Node and pnpm once before the app can install a plugin: install Node LTS, then
npm install -g pnpm. Mitsumeru ships no package manager, and DSH forwards every plugin install to pnpm in the
profile directory. The full runbook for a Windows machine is
docs/status/2026-09-26-aria-windows-install.md in the product workspace.
Mitsumeru 0.2.2
Production build — latest channel, like 0.2.0 and 0.2.1. This release ships three
targets: Apple Silicon, Intel, and Windows x64.
Intel and Windows are back, and this time they work
0.1.9-dev's Intel dmg and Windows exe have never started. 0.2.0 and 0.2.1 shipped
arm64 only, by decision, because the alternative was handing out builds that could
not launch. Both facts had the same cause, and it is fixed here.
prepare-harness.sh staged the host closure with no platform handling at all, and
electron-builder copies that ONE tree into every target it builds. So the x64 app
and the Windows exe both received Apple Silicon native addons —
node-addon-require-builtin-darwin-arm64 — and an x86_64 process cannot load arm64
code. Rosetta does not go that direction. The build log had been listing every
platform-specific dependency it could not bundle, in every build:
platform-specific optional dependencies not bundled … node-addon-require-builtin-darwin-x64, … win32-x64-msvc, @koromix/koffi-darwin-x64, @vscode/ripgrep-darwin-x64, @img/sharp-darwin-x64 …
Staging is now per target (--os / --cpu, writing supportedArchitectures into
the stage), and the build is per target too, so what gets packaged is what was
staged:
| Artifact | Architecture |
|---|---|
Mitsumeru-0.2.2-arm64.dmg / -arm64-mac.zip |
Apple Silicon |
Mitsumeru-0.2.2.dmg / -mac.zip |
Intel (x86_64) |
Mitsumeru Setup 0.2.2.exe |
Windows x64 |
Two further traps had to go, both of which silently produced the wrong build: the
notarize step deleted every artifact and repacked from release/mac-arm64 (that
is how a two-arch release became arm64-only in 0.2.0 and 0.2.1), and the repackage
invocation had no arch flag, so electron-builder packaged for the host and
handed back arm64 artifacts for an Intel app. Both are fixed and both are now
asserted rather than trusted.
Windows is unsigned in this release
Mitsumeru Setup 0.2.2.exe carries no Authenticode signature — there is no
Windows code-signing certificate on this rig, and the certificate is a purchase
with identity validation (Azure Artifact Signing is the cheapest path at ~$10/mo;
OV certificates start around $150/yr). Expect "Windows protected your PC" on
first run: More info → Run anyway. That is the unsigned path behaving as
documented, not a packaging fault.
Worth knowing before buying: EV certificates stopped bypassing SmartScreen in
2024, so paying the EV premium for that reason is no longer justified. Any valid
certificate still helps — reputation accumulates against a consistent publisher
identity and carries across releases, while unsigned builds start from zero every
version.
Gates
pnpm verify:release— 36 checks, 0 failures, run once per architecture: both
apps signed with the Developer ID and carrying a secure timestamp, both dmgs
accepted by Gatekeeper and stapled, the app inside each zip and inside
each dmg carrying a stapled ticket and passing Gatekeeper, and all four
latest-mac.ymlentries matching sha512 and size on disk. The gate also fails a
feed that lists fewer than four artifacts — the shape an Intel client experiences
as "no update available".pnpm verify:arch— each app's Mach-O architecture, the harness addon it
must load, and the absence of the other architecture's addon; then a real boot
of the packaged harness on the package's own Electron. The Intel app is booted
under Rosetta, and it serves its page.PROMOTE=1 pnpm check:label— 8/8, including all four artifacts carrying the
version and the packaged app reporting it.
Known, not fixed in this release
- Windows is unsigned (above).
verify:survives-updatestill fails on@muen/dsh-white-labelimporting
@deepseek-ai/schemasterywithout declaring it, so installing that plugin from
source cannot resolve it. Carried from 0.2.0; the shipped app is unaffected.@muen/dsh-codex-fold's Settings card ("Status text") cannot render on DSH
0.1.6-alpha.2:settings.plugin.itemis declared by no bundle. Its folding is
unaffected.- The download page's Windows row stays off until the installer is signed. The page
itself is already architecture-aware and deployed (2026-09-18): it picks the Intel dmg
when the browser reports x86, recommends the matching row, and lists the other
architecture beside it. One step happens with this publication — the static fallback
hrefmoves from 0.2.1 to 0.2.2, becauseverify:distributiondownloads the fallback
rather than the release the page resolves at load.
Install
- Apple Silicon:
Mitsumeru-0.2.2-arm64.dmg - Intel:
Mitsumeru-0.2.2.dmg - Windows:
Mitsumeru-Setup-0.2.2.exe(unsigned — see above)
macOS builds are notarized and stapled, so they open on a double-click.
latest-mac.yml and latest.yml are the update manifests and ship beside the binaries.
The .exe asset name is hyphenated on purpose: electron-builder writes that name into
latest.yml's url, and the Windows updater downloads what the manifest names. The local
build output keeps the space (Mitsumeru Setup 0.2.2.exe); only the uploaded asset is
renamed.
Mitsumeru 0.2.1
Production build — the -dev suffix stays off and this leaves the latest
channel, like 0.2.0. Version 0.2.1 adds the quality-of-life plugin set.
This release is arm64-only, deliberately, for the same measured reason as 0.2.0.
prepare-harness.sh stages one host closure for every target, so the x64 and Windows
builds receive Apple Silicon native addons and an Intel process cannot load them. The
manifest lists only arm64 artifacts, so an Intel client finds no suitable file and stays
where it is rather than being handed a build that will not start. Intel and Windows
remain on 0.1.9-dev until the staging learns platform handling.
What's new: the daily-driver set
Four plugins built to make Mitsumeru livable as the every-day app — the surfaces that
were being missed after moving off Kun — now ship inside the build:
- Turn summary — one muted, localized row per finished turn: Processed 1m 26s ·
12 steps · Edited 3 files. It expands into what the turn actually did. - Changes card — a changed-files card at the end of a turn: Edited 3 files +6 −1
with per-file rows, computed from the turn's own file tools, so it also appears where
the Host's change recorder announced nothing. - Codex fold — finished turns fold, and the thinking/tool/context runs between prose
collapse into one summary chip per run; expanding shows the native rows again. - Context reminder — a full-width banner above the composer once a session passes a
model-aware threshold (max(floor, min(percent% × window, ceiling)), default 35% capped
at 300K and floored at 25K), offering a fresh session, a token-banded snooze, or
dismissal — plus Settings → General → Context reminder, the row that owns the policy.
The first three are Client-only surfaces over the conversation; the fourth adds one
preference row and reads the same contextPressure projection the harness's own context
meter reads. Each has an empty Host half, so nothing about them can drift from the
harness's own state.
The set is composed into the app, not offered by it
SHIPPED_PLUGINS in the shell is one literal list, and ensureProfile recomputes the
profile's bundle list from it on every launch. So an app that is already installed
gains these four on the next start, without deleting the profile and without touching
sessions, settings or the brand. They are vendored into the harness resource by
prepare-harness.sh — that is, they sit inside the code signature, rather than being
added to an installed app afterwards.
What deliberately does not ship
dsh-auto-collapse (community). Codex fold is its localized fork, and the two fold
the same runs: shipping both double-folds a single conversation. The dev profile had the
community package installed (already uncomposed) and it has been removed. Everything else
community stays where it belongs — in the market, installed per profile:
- browser-skill needs its Chrome extension, so it cannot work as a baked-in surface.
- codex-connect is a third-party OAuth provider that updates itself from the registry;
vendoring it would freeze an alpha inside a signed artifact and make its updates ours.
The rule this makes explicit: the app ships Muen's own plugins; the market serves the
community's.
One visual defect fixed on the way in
A folded-work summary chip was rendering one size larger than every native row around
it — 14px where the harness's rows are 13px — and it did so invisibly: the chip's
stylesheet declared the right tier, but the chip sets font: inherit inline on a button
inside the conversation body, and the inline declaration wins.
The chip now follows the harness's published row contract (DisclosureRow.module.css:
title on the secondary tier, row height / leading box / glyph edge shifted by
--dsh-content-font-delta), so it also tracks the Settings font-size preference the way
native rows do. The distinction between our rows and the harness's moves to colour, where
it belongs: our labels stay one step dimmer, and the chip's detail — previously brighter
than its own label — takes the caption tier.
Where the source lives
The four are published from
muen-collective/muen-plugins
(plugins/dsh-turn-summary, dsh-changes-card, dsh-codex-fold,
dsh-context-watchdog), and vendored into this app as byte-identical mirrors. They were
developed as workspace links with no version history; this release is their promotion.
The sidebar brand seat no longer picks its owner by accident
@muen/dsh-brand-mitsumeru and @muen/dsh-white-label both registered the same
three exclusive seats — sidebar.brand.mark, sidebar.brand.name and
conversation.hero.brand.mark — at the same priority. A single slot does not merge
those, it refuses the second registration with an uncaught error, so one of the two
threw on every launch (visible only in a console nobody reads), and which mark you saw
came down to registration order. It had been that way since 0.2.0.
The owner of those seats is now a decision rather than a race: the configuration layer
owns them when a brand is actually configured — an uploaded mark, or the filesystem
brand folder — and the product mark keeps them otherwise. White-label claims them at a
lower priority, which is how this slot system expresses a deliberate shadow, and only
when there is a brand to show: every one of those renderers returns nothing without a
brand source, so claiming the seats unconditionally would have replaced the Mitsumeru
mark with empty space. The hero tagline seat moved out of that chain entirely — it is
uncontested, and it must not depend on the three contested registrations succeeding.
Measured by the mount gate in a fresh profile: one console error before, none after.
Gates
pnpm verify:release— 16/16.codesign --verify --deep --strict; the Developer ID
signature and its secure timestamp; every Mach-O binary inside the harness signed;
Gatekeeper on the app and on the dmg;stapler validateon both; the app inside the
zip and inside the dmg carrying a stapled ticket; and everylatest-mac.ymlentry
matching the bytes on disk.PROMOTE=1 pnpm check:label— 6/6 in promotion mode:0.2.1carries no-dev
suffix, both artifacts carry the version, the packaged app reports it, and no-dev
version is named in these notes.pnpm verify:mount— widened this release from one representative package to the
composition the app actually writes: the same seven bundles, a throwaway profile, and
assertions read back from a real renderer. Seven bundles composed and served; Settings →
General shows the EVA theme row, both skins and the Context reminder row; zero
console errors from any shipped bundle.pnpm verify:surfaces— the sidebar brand seat carries our mark, with no upstream
occupant left in it.pnpm verify:distribution— 12/12 against the published release and the live
download page: the site targets the canonicalmitsumeru-apprepo, its hero fallback and
the live release name the same artifact, and the dmg a client actually downloads returns
200, matcheslatest-mac.yml's sha512, passes Gatekeeper as a notarized Developer ID
build, carries a stapled ticket, and reports 0.2.1.
Known, not fixed in this release
verify:survives-updatefails:@muen/dsh-white-labelimports
@deepseek-ai/schemasterywithout declaring it, so installing that plugin from
source cannot resolve it. The shipped app is unaffected — the vendored copy resolves
it beside the harness tree. Carried from 0.2.0; the fix is declaring the dependency.@muen/dsh-codex-fold's Settings card ("Status text") cannot render on this harness:
it registers intosettings.plugin.item, which no client bundle declares in DSH
0.1.6-alpha.2 (the name exists only in a type comment), so the inject waits forever. Its
folding is unaffected. The mount gate asserts this fact rather than skipping the row.- The marketing site's Vercel project no longer deploys on push: the live page was still
the Sep 17 build, so the site's own Sep 18 commit had never gone live either. Both
are published now, deployed with the CLI, but the Git integration itself still needs
reconnecting — the next site commit will not appear on its own. - A plugin installed from a
link:directory keeps its dependency spec when its contents
change, soplugin_manager install_bundlereportsambiguous-installfor a linked
plugin whose bytes changed. The link already serves the new files; only a page reload is
needed. Packed/tarball installs are unaffected.
Install
Download Mitsumeru-0.2.1-arm64.dmg, open it, drag Mitsumeru to Applications. It is
notarized and stapled, so it opens on a double-click.
latest-mac.yml is the update manifest and must be uploaded beside the binaries.
Mitsumeru 0.2.0
Mitsumeru 0.2.0 — Apple Silicon (arm64).
Wraps DeepSeek Harness 0.1.6-alpha.2 and Electron 44.0.0. Signed with Developer ID
(THUY QUANG PHAM, 4Q6GC57QG4, secure timestamp), notarized by Apple, and the app
carries a stapled ticket both inside the dmg and inside the zip.
The two fixes that were missing from every published build
Copy buttons work. Every Copy control — code blocks, tool results, diffs, terminal
output — was a click that did nothing. The cause was ours, in the shell: the app
installed a lockdown permission handler that denied every permission request, and
Chromium asks for clipboard-sanitized-write before the async Clipboard API will
write. The harness's copy helper catches that rejection and returns false, and its
caller returns early — so the button neither wrote nor showed its "Copied" state, and
there was no error to see. The fix keeps the posture and names one exception;
clipboard-read is deliberately still denied.
The hero tagline is part of the brand now, and it ships. The blank-session headline
above the composer is brand copy, so @muen/dsh-white-label owns it: Settings → Brand
→ Hero tagline, up to 200 characters, persisted as brandTagline. Empty keeps the
shipped headline.
The headline has no slot and a plugin cannot declare one, so the seat is a narrow
additive patch to the vendored client bundle: the renderSlot("conversation.hero.tagline")
call in HeroShell and the seat's declaration in the conversation.content
factory's children table. The call without the declaration blanked every new session
on 2026-09-17. The build patches the staged harness before signing, so the seam is
inside the code signature — the previous route, patching an installed app, cost the
signature and left a .muen-unpatched sidecar behind.
Why you could not see them in 0.1.9-dev
v0.1.9-dev was built at 13:05 from c0f175a. The copy-button fix (d0a6d0e) and the
hero-tagline seam (ab21bd7) were committed at 21:44 the same day — eight hours
after the artifacts were built. Neither is in any published artifact, and this version
bump is the first build to contain them.
This release is arm64-only, deliberately
The Intel (x64) and Windows targets are not shipped. prepare-harness.sh stages the
host closure and has no platform handling, so every target carries Apple Silicon native
addons: the x64 app's node-addon-require-builtin-darwin-arm64/prebuilt/darwin-arm64-napi-v9.node
is a Mach-O arm64 bundle, and the loader resolves its prebuilt from
${process.platform}-${process.arch} — on Intel it asks for darwin-x64 and finds
nothing. An x86_64 process cannot load arm64 code, and Rosetta does not go that
direction. Shipping it would hand out a build that does not start.
latest-mac.yml therefore lists only the arm64 artifacts, so an Intel client finds no
suitable file and stays where it is rather than being handed a broken build. Intel and
Windows remain on 0.1.9-dev until the staging is fixed. (This is not new: 0.1.9-dev's
Intel dmg and Windows exe carry the same defect, so neither has ever worked.)
Gates
pnpm verify:release — 16/16, including the signature, the secure timestamp, Gatekeeper
on the app and on the dmg, stapler validate on the app and the dmg, the app inside
the zip and inside the dmg carrying a stapled ticket, and every latest-mac.yml
entry matching the bytes on disk. PROMOTE=1 pnpm check:label — 6/6. pnpm verify:mount
and pnpm verify:surfaces pass.
Known, not fixed in this release
verify:survives-updatefails:@muen/dsh-white-labelimports@deepseek-ai/schemastery
(introduced ine9b2000) without declaring it, so installing the plugin from source
cannot resolve it. The shipped app is unaffected — the vendored copy resolves it beside
the harness tree. The fix is declaring the dependency.verify:survives-updateis not listed inpackage.json, so the documented
pnpm verify:survives-updatefails with "Command not found". Run it as
bash scripts/verify-survives-update.sh.- The marketing site's hero fallback
hrefstill points atv0.1.9-dev. Live resolution
(GitHub latest) picks 0.2.0 up; the static fallback needs the site repo.
Install
Download Mitsumeru-0.2.0-arm64.dmg, open it, drag Mitsumeru to Applications. It is
notarized and stapled, so it opens on a double-click — no right-click needed.
latest-mac.yml is the update manifest and must be uploaded beside the binaries.