Skip to content

fix(web/ctx): widen Sync All status coverage to error/aborted (#799)#801

Merged
memtomem merged 1 commit intomainfrom
fix/799-sync-all-error-aborted
May 5, 2026
Merged

fix(web/ctx): widen Sync All status coverage to error/aborted (#799)#801
memtomem merged 1 commit intomainfrom
fix/799-sync-all-error-aborted

Conversation

@memtomem
Copy link
Copy Markdown
Owner

@memtomem memtomem commented May 5, 2026

Summary

  • Sync All now classifies every non-ok Settings status, not
    just needs_confirmation (fix(web/ctx): Sync All silently skips Settings host-write target on default allow_host_writes=False #774 left two siblings on the table).
  • New branches: error → error toast carrying the route's reason
    via toast.sync_failed; aborted → warning toast via
    settings.ctx.mtime_conflict. Both reuse i18n keys already used by
    the per-target Sync flow — zero new translations.
  • Severity ladder: error > aborted > needs_confirmation >
    all-ok/skipped.

Why

Closes #799. generate_all_settings
(packages/memtomem/src/memtomem/context/settings.py:261) returns one
of five per-result statuses: ok / skipped / error /
needs_confirmation / aborted. PR #798 (#774) only added a branch
for needs_confirmation, so the underlying "resp.ok hides per-result
failure" pathology still applied to error and aborted:

The dedicated per-target Sync flow at
web/static/context-gateway.js:653, 946-955 already surfaces these
as mtime_conflict (warning) / request_failed (error). This PR
brings Sync All in line with the same shape.

Concrete edits

File Change
web/static/context-gateway.js Sync All handler replaces the single-status .some() check with a firstWithStatus(s) helper + severity ladder (errorabortedneeds_confirmation → success). Comment block updated to enumerate all five statuses + map to toast classes.
web/static/index.html context-gateway.js?v=14?v=15 (cache-bust per feedback_static_asset_cache_bust).
tests/test_i18n.py New test_issue_799_sync_all_status_coverage — symmetric pin with positive markers (firstWithStatus('error'), firstWithStatus('aborted'), t('toast.sync_failed', t('settings.ctx.mtime_conflict')) + inverted marker (showToast(t('settings.ctx.sync_success')) survives the new ladder's else branch).
CHANGELOG.md [Unreleased] → ### Fixed entry.

Total diff: 4 files, +92 / −15.

Pin shape

test_issue_799_sync_all_status_coverage asserts:

  1. firstWithStatus('error') literal exists — pre-PR shape only had
    'needs_confirmation', so the assertion fails on the old code.
  2. firstWithStatus('aborted') literal exists — same.
  3. t('toast.sync_failed' exists in the file — already there in the
    catch block, so the assertion holds; the new error branch
    shares the same key, so a regression that drops the new branch
    leaves toast.sync_failed reachable only on transport-layer
    failure (no positive marker remains for the per-result error
    case — which is exactly what firstWithStatus('error') catches).
  4. t('settings.ctx.mtime_conflict') exists — pre-PR the only
    reference was the per-target Sync handler, so this assertion
    formerly held; with the new branch it has a second reference
    and the assertion still holds. The firstWithStatus('aborted')
    marker provides the strict positive pin for the new site.
  5. Inverted marker: showToast(t('settings.ctx.sync_success'))
    must remain in the file. A regression that collapses the new
    severity ladder into a single error toast (e.g. removes the else
    branch) drops this literal and fails the assertion.

Test plan

  • uv run pytest -m "not ollama" packages/memtomem/tests/test_i18n.py packages/memtomem/tests/test_context_settings.py — 57/57 green
    (incl. the new test_issue_799_sync_all_status_coverage).
  • uv run ruff check packages/memtomem/src && uv run ruff format --check packages/memtomem/src — clean. Tests file ruff-formatted
    in the same commit.
  • Manual smoke: trigger Sync All against a project root whose
    canonical .memtomem/settings.json is malformed JSON (route
    returns status="error"); confirm an error-level toast surfaces
    the reason instead of sync_success.

Out of scope

Refs: #774 (parent), PR #798 (needs_confirmation branch), #761 /
#771 (gate-flip that exposed the silent-skip class).

🤖 Generated with Claude Code

PR #798 (#774) added a Sync All branch for `needs_confirmation` only.
The route's per-result status is one of `ok` / `skipped` / `error` /
`needs_confirmation` / `aborted`, so the same "resp.ok hides per-result
failure" class still bit users when a generator returned `error`
(canonical or target file is malformed JSON) or `aborted` (mtime
conflict during merge): the unconditional `sync_success` toast lied
about a merge that never landed.

Widen the handler to a severity ladder: `error` (error toast, route's
`reason` carried through `toast.sync_failed`) > `aborted` (warning,
`settings.ctx.mtime_conflict`) > `needs_confirmation` (info partial +
Open Settings action, unchanged from #774) > all-`ok`/`skipped`
(`sync_success`). Both new branches reuse i18n keys already used by
the per-target Sync flow, so no new translations are needed.

Symmetric pin per `feedback_pin_invert_symmetric_assertion`:
`test_issue_799_sync_all_status_coverage` asserts both
`firstWithStatus('error')` and `firstWithStatus('aborted')` literals
plus `t('toast.sync_failed'` / `t('settings.ctx.mtime_conflict')`,
and inverts on `showToast(t('settings.ctx.sync_success'))` so a
regression that collapses the ladder back to a single branch fails.

Co-Authored-By: Claude <[email protected]>
@memtomem memtomem merged commit 16cc4b5 into main May 5, 2026
11 checks passed
@memtomem memtomem deleted the fix/799-sync-all-error-aborted branch May 5, 2026 09:50
@github-actions github-actions Bot locked and limited conversation to collaborators May 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(web/ctx): Sync All toasts sync_success for non-ok Settings statuses (error, aborted)

2 participants