Background
ADR-0001 §5 c4 ("conflict path covered by a test fixture") requires
either an HTTP 409 fixture (Skills' optimistic-locking design) or a
documented soft-abort response. Phase B (Commands) shipped before §5
was authored (RFC #761 / ADR landed in #769) and lacks an HTTP-layer
stale-mtime 409 fixture parallel to Skills and Agents.
Per ADR-0001 §5: "§5 applies to future phase promotions. It does
not retroactively fail Phases A–C — those shipped under earlier
review and gaps … are tracked as hygiene follow-ups, not regressions."
This is the hygiene follow-up for Phase B. (The Phase C sibling
#773 was closed as already-covered — Agents has the test at
tests/test_web_routes_context.py:772.)
Current 409 coverage map
| Phase |
Stale-mtime 409 fixture |
Concurrent PUT race 409 |
| A Skills |
✓ test_web_routes_context.py:224 (TestUpdateSkill.test_mtime_conflict) |
✓ via TYPE_MATRIX at test_web_routes_context_mutators.py:405 |
| B Commands |
✗ MISSING — this issue |
✓ via TYPE_MATRIX |
| C Agents |
✓ test_web_routes_context.py:772 (TestAgentCRUD.test_mtime_conflict_after_external_write) |
✓ via TYPE_MATRIX |
The concurrent-PUT race test at mutators.py:405 is a different
shape — two parallel PUTs serialize under the in-process lock and
the second 409s. The §5 c4 fixture this issue tracks is the
stale-mtime flavor: a single PUT with a mtime_ns from before
an external write to the runtime file, mirroring Skills' and Agents'
existing patterns.
Scope
Add one new test method to TestCommandCRUD in
packages/memtomem/tests/test_web_routes_context.py (the class at
line 552, between test_create_update_delete at line 554 and
TestSyncCommands at line 579). Mirror the Agents pattern verbatim
— the PUT body shape ({"content": "...", "mtime_ns": "..."}) and
the assertion set (HTTP 409, status: "aborted", mtime_ns echo,
file-content-unchanged check) are identical between Skills/Agents,
so the diff is mostly fixture-setup substitution.
Helper picker:
- Use
_make_command (already defined at
test_web_routes_context.py:454) instead of _make_skill or
_make_agent. Commands are single-file artifacts (<name>.md),
not folders.
- Sync via
POST /api/context/commands/sync (matches the existing
pattern at :779 for Agents).
- Target path:
tmp_path / ".claude" / "commands" / "<name>.md".
The PUT body / assertion section can be copied byte-for-byte from
the Agents test at :791-802, with the URL changed from
/api/context/agents/<name> to /api/context/commands/<name> and
the content shape changed from agent frontmatter to command
frontmatter.
The 409 path itself already exists in production code at
packages/memtomem/src/memtomem/web/routes/context_commands.py:241-250
(current_mtime_ns != body_mtime_ns → 409 + status: "aborted",
verbatim identical to the Skills handler at
context_skills.py:199-208). This issue is purely test-coverage
backfill.
Acceptance
Out of scope
- Implementation changes to Commands sync routes — the 409 path
already exists in production code. This issue is purely
test-coverage backfill.
- Phase A / C — already covered (see map above).
Refs: #761 (parent RFC, closed); #773 (Phase C sibling, closed as
already-covered).
Background
ADR-0001 §5 c4 ("conflict path covered by a test fixture") requires
either an HTTP 409 fixture (Skills' optimistic-locking design) or a
documented soft-abort response. Phase B (Commands) shipped before §5
was authored (RFC #761 / ADR landed in #769) and lacks an HTTP-layer
stale-mtime 409 fixture parallel to Skills and Agents.
Per ADR-0001 §5: "§5 applies to future phase promotions. It does
not retroactively fail Phases A–C — those shipped under earlier
review and gaps … are tracked as hygiene follow-ups, not regressions."
This is the hygiene follow-up for Phase B. (The Phase C sibling
#773 was closed as already-covered — Agents has the test at
tests/test_web_routes_context.py:772.)Current 409 coverage map
test_web_routes_context.py:224(TestUpdateSkill.test_mtime_conflict)TYPE_MATRIXattest_web_routes_context_mutators.py:405TYPE_MATRIXtest_web_routes_context.py:772(TestAgentCRUD.test_mtime_conflict_after_external_write)TYPE_MATRIXThe concurrent-PUT race test at
mutators.py:405is a differentshape — two parallel PUTs serialize under the in-process lock and
the second 409s. The §5 c4 fixture this issue tracks is the
stale-mtime flavor: a single PUT with a
mtime_nsfrom beforean external write to the runtime file, mirroring Skills' and Agents'
existing patterns.
Scope
Add one new test method to
TestCommandCRUDinpackages/memtomem/tests/test_web_routes_context.py(the class atline 552, between
test_create_update_deleteat line 554 andTestSyncCommandsat line 579). Mirror the Agents pattern verbatim— the PUT body shape (
{"content": "...", "mtime_ns": "..."}) andthe assertion set (HTTP 409,
status: "aborted",mtime_nsecho,file-content-unchanged check) are identical between Skills/Agents,
so the diff is mostly fixture-setup substitution.
Helper picker:
_make_command(already defined attest_web_routes_context.py:454) instead of_make_skillor_make_agent. Commands are single-file artifacts (<name>.md),not folders.
POST /api/context/commands/sync(matches the existingpattern at
:779for Agents).tmp_path / ".claude" / "commands" / "<name>.md".The PUT body / assertion section can be copied byte-for-byte from
the Agents test at
:791-802, with the URL changed from/api/context/agents/<name>to/api/context/commands/<name>andthe content shape changed from agent frontmatter to command
frontmatter.
The 409 path itself already exists in production code at
packages/memtomem/src/memtomem/web/routes/context_commands.py:241-250(
current_mtime_ns != body_mtime_ns → 409 + status: "aborted",verbatim identical to the Skills handler at
context_skills.py:199-208). This issue is purely test-coveragebackfill.
Acceptance
TestCommandCRUD(
test_web_routes_context.py) exercises the stale-mtime 409path via
PUT /api/context/commands/<name>.(canonical sync → external
os.utimebump → stale-mtime PUT→ assert HTTP 409 +
status: "aborted"+ mtime_ns echo +content-unchanged).
uv run pytest -m "not ollama" tests/test_web_routes_context.pygreen.
Out of scope
already exists in production code. This issue is purely
test-coverage backfill.
Refs: #761 (parent RFC, closed); #773 (Phase C sibling, closed as
already-covered).