Skip to content

fix(web/ctx): echo current mtime_ns on Settings resolve 409 abort#782

Merged
memtomem merged 1 commit intomainfrom
fix/settings-resolve-409-mtime-echo
May 4, 2026
Merged

fix(web/ctx): echo current mtime_ns on Settings resolve 409 abort#782
memtomem merged 1 commit intomainfrom
fix/settings-resolve-409-mtime-echo

Conversation

@memtomem
Copy link
Copy Markdown
Owner

@memtomem memtomem commented May 4, 2026

Summary

  • Behavioral fix: Settings resolve route now echoes the current st_mtime_ns in its 409 abort envelope, matching the Skills/Commands/Agents contract. This lets clients refresh local state on conflict without an extra round-trip.
  • Test tightened to ADR-0001 §5 c4 triplet: response status + envelope mtime_ns echo + no-write content (asserted symmetrically with positive marker + negative !=).
  • Mutation-validated: dropping the envelope echo or moving _write_json above the recheck both flip the test red.

Why

Codex review on the three-phase parity series (#776/#777/#779/#781) flagged Settings as the lone surface that was not asserting the strict triplet. The reviewer's inspection of settings_sync.py:262 confirmed it was a real envelope-shape gap — the recheck already re-stat()'d the target, so adding the echo is one line. Tightening the test alone (without the route fix) would have papered over an asymmetric API surface.

Surface status: "aborted" no-write content mtime_ns echo
Skills (PR #779)
Commands (PR #781)
Agents (PR #776)
Settings — before this PR
Settings — after this PR

Compatibility

  • mtime_ns is additive in the response. The only known consumer (settings-hooks-watchdog.js:123–139) reads result.status and result.reason — adding a new key cannot break it.
  • Reason text wording is unchanged.

Test plan

  • uv run pytest packages/memtomem/tests/test_context_settings.py — 37 passed
  • Wider sweep across test_web_routes_context*.py + test_context_settings.py — 104 passed
  • All settings-keyword tests across the suite — 62 passed
  • uv run ruff check + uv run ruff format --check — clean
  • Mutation 1 (drop envelope echo) → KeyError: 'mtime_ns' ✓ caught
  • Mutation 2 (write before recheck) → assert 'echo user' in on_disk fails ✓ caught

🤖 Generated with Claude Code

Settings ``POST /api/context/settings/resolve`` returned only
``status``/``reason`` on stale-mtime abort, while Skills/Commands/Agents
all echo the current ``st_mtime_ns`` in their 409 envelopes (PRs
#776/#777/#779/#781). This was a parity gap, not an intentional
contract: the route already had the old ``mtime_ns`` and the recheck
already re-stat()'d the target, so adding the echo is a one-line
behavioral fix that lets clients refresh local state without an extra
round-trip.

Tighten ``test_resolve_route_returns_soft_abort_on_stale_mtime`` to
the same triplet the sibling routes pin:

* response status == "aborted"
* envelope ``mtime_ns`` == current ``st_mtime_ns``
* no-write content, asserted symmetrically: "echo user" still present
  AND "echo canonical" absent (catches a regression that wrote before
  returning aborted, which a status/reason-only assertion would miss).

Mutation-validated both new assertions against the production code
before committing — dropping the envelope echo raises ``KeyError`` on
``body["mtime_ns"]``; reordering ``_write_json`` above the recheck
flips the "echo user" present check.

Co-Authored-By: Claude <[email protected]>
@memtomem memtomem merged commit 9ec1915 into main May 4, 2026
11 checks passed
@memtomem memtomem deleted the fix/settings-resolve-409-mtime-echo branch May 4, 2026 22:47
@github-actions github-actions Bot locked and limited conversation to collaborators May 4, 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.

2 participants