Skip to content

fix(web/ctx): Sync All silently skips Settings host-write target on default allow_host_writes=False #774

@memtomem

Description

@memtomem

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:

  1. 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.

  2. 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

  • Sync All no longer shows sync_success when Settings result
    is needs_confirmation.
  • User has a clear path to drive the host-write confirmation
    (either via the toast offering navigation, or via an inline
    flow).
  • No regression to the artifact (Skills / Commands / Agents)
    fanout — those continue to all-or-nothing.

Refs: #761 (parent RFC, closed); #771 (introducing prod surface).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions