Symptom
After RFC #761 PR-3 landed (#771, squash 90d884f), the Sync All
button (mm web Context Gateway → "Sync All") fans out to four
endpoints sequentially: Skills, Commands, Agents, Settings. The
Settings hop posts to /api/context/settings/sync with no body.
Without a body, ApplySettingsSyncRequest.allow_host_writes defaults
to False. For host-write targets like ~/.claude/settings.json,
the route returns HTTP 200 with body {"results": [{"name": "claude_settings", "status": "needs_confirmation", ...}]} — the
merge is skipped.
The current frontend at web/static/context-gateway.js:
const settingsResp = await fetch('/api/context/settings/sync', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
});
if (!settingsResp.ok) throw new Error('Settings sync failed');
resp.ok only checks HTTP status; a 200 with needs_confirmation
results passes the guard. The user sees the sync_success toast
even though ~/.claude/settings.json was not actually merged. The
post-merge toast lies.
Pre-existing severity, new audience
This silent-skip behavior pre-dates RFC #761 — it existed in dev
mode behind the STATE.uiMode === 'dev' gate. PR #771 did not
introduce the bug; it expanded the audience from mm web --dev
maintainers to all prod users. The PR-771 review correctly flagged
this as out-of-scope (preserving pre-existing behavior keeps the
flip surgical) and recommended a follow-up.
Proposed fix
Inspect the per-result status in the response body and surface
partial-success to the user. Two reasonable shapes:
-
Lightweight: if any result has status: 'needs_confirmation',
change the toast from sync_success to an info-level toast like
"Sync All complete except Settings — confirm host writes in the Settings panel" and offer a click to navigate.
-
Full: the dedicated Settings panel already has a host-write
confirmation dialog. Sync All could trigger that dialog
automatically when host-write results are pending, then re-post
/api/context/settings/sync with allow_host_writes: true after
the user confirms.
Option 1 is the smaller change and matches the existing UX
expectation that Sync All is a quick fanout (no inline modal flow).
Option 2 is more polished but expands Sync All's responsibility.
Either approach should re-use the existing
settings.hooks.needs_confirmation i18n family — verify keys exist
before adding new ones.
Acceptance
Refs: #761 (parent RFC, closed); #771 (introducing prod surface).
Symptom
After RFC #761 PR-3 landed (#771, squash 90d884f), the Sync All
button (
mm webContext Gateway → "Sync All") fans out to fourendpoints sequentially: Skills, Commands, Agents, Settings. The
Settings hop posts to
/api/context/settings/syncwith no body.Without a body,
ApplySettingsSyncRequest.allow_host_writesdefaultsto
False. For host-write targets like~/.claude/settings.json,the route returns HTTP 200 with body
{"results": [{"name": "claude_settings", "status": "needs_confirmation", ...}]}— themerge is skipped.
The current frontend at
web/static/context-gateway.js:resp.okonly checks HTTP status; a 200 withneeds_confirmationresults passes the guard. The user sees the
sync_successtoasteven though
~/.claude/settings.jsonwas not actually merged. Thepost-merge toast lies.
Pre-existing severity, new audience
This silent-skip behavior pre-dates RFC #761 — it existed in dev
mode behind the
STATE.uiMode === 'dev'gate. PR #771 did notintroduce the bug; it expanded the audience from
mm web --devmaintainers to all prod users. The PR-771 review correctly flagged
this as out-of-scope (preserving pre-existing behavior keeps the
flip surgical) and recommended a follow-up.
Proposed fix
Inspect the per-result
statusin the response body and surfacepartial-success to the user. Two reasonable shapes:
Lightweight: if any result has
status: 'needs_confirmation',change the toast from
sync_successto an info-level toast like"Sync All complete except Settings — confirm host writes in the Settings panel"and offer a click to navigate.Full: the dedicated Settings panel already has a host-write
confirmation dialog. Sync All could trigger that dialog
automatically when host-write results are pending, then re-post
/api/context/settings/syncwithallow_host_writes: trueafterthe user confirms.
Option 1 is the smaller change and matches the existing UX
expectation that Sync All is a quick fanout (no inline modal flow).
Option 2 is more polished but expands Sync All's responsibility.
Either approach should re-use the existing
settings.hooks.needs_confirmationi18n family — verify keys existbefore adding new ones.
Acceptance
sync_successwhen Settings resultis
needs_confirmation.(either via the toast offering navigation, or via an inline
flow).
fanout — those continue to all-or-nothing.
Refs: #761 (parent RFC, closed); #771 (introducing prod surface).