Skip to content

test(admission): a cancelled real remember keeps its slot and lands its row in the Cortex store (#728, slice 4) - #943

Merged
github-actions[bot] merged 3 commits into
mainfrom
fix/728-cancel-remember-cortex-store
Nov 7, 2026
Merged

github-actions[bot] merged 3 commits into
mainfrom
fix/728-cancel-remember-cortex-store

Conversation

@cdeust

@cdeust cdeust commented Nov 7, 2026

Copy link
Copy Markdown
Owner

Summary

Slice 4 of #728, the first remaining slice listed after #936: the same MCP cancel and disconnect scenario, now with a real mutating tool on an isolated Cortex store.

#936 drove notifications/cancelled and client disconnect through the MCP SDK client and server (Client(mode="legacy"), in-memory transport), but with a synthetic handler writing to a plain SQLite file. tests_py/server/test_mcp_protocol_cancellation_remember.py builds the server with the production registration (tool_registry_memory.register), calls the real remember handler, and writes to the suite's isolated Cortex SQLite store (tests_py/conftest.py redirects CORTEX_MEMORY_DB_PATH before any import). The only test double is a gate on the store instance's insert_memory_exact. The handler has already run its write gate and curation when it blocks, right before the row is written.

Both tests (cancel, disconnect) check four things:

  • the worker keeps the remember admission slot until it ends;
  • abandoned_work counts it while it runs, then records it completed;
  • nothing is written before the release (count_memories()["total"] unchanged);
  • after the release, the memory row lands with the sent content and the total goes up by one.

Refs #728 (not Closes: one slice remains, see below).

Type of change

  • New feature (non-breaking change which adds functionality): tests only, no production code changes.

Test plan

  • New test file: 2 tests. Green 5 runs in a row (2 passed each, about 2.2 s).
  • Mutant: sem.release() added to the CancelledError branch of admission.run_admitted (gives the slot back at the cancel). Both tests fail with assert 4 == (4 - 1). File restored afterwards.
  • Neighbours: tests_py/server, test_admission.py, test_admission_cancellation.py, test_abandoned_work_status.py, test_remember.py: 72 passed.
  • ruff check / ruff format --check (0.17.3, the version in uv.lock) on the new file: OK. scripts/check_craftsmanship.py: OK.
  • Full pytest suite not run: the environment was installed without torch (its download host is blocked here), so the embedding tests would fail for reasons unrelated to this PR.

Audit notes

Coding-standards compliance

  • §2.2 Layer dependency direction preserved (test file only).
  • §8 Constants (_GUARD, _POLL) carry a source: note.
  • §9 No dead code, no TODO.

Breaking changes

None.

Reviewer checklist

  • CHANGELOG.md updated (Unreleased / Added).
  • No secrets / credentials / PII in the diff.
  • CI passes on the latest commit: not yet observed.

Generated by Claude Code

…ts row in the Cortex store (#728, slice 4)

Slice 3 drove notifications/cancelled and client disconnect through the
MCP SDK client and server with a synthetic handler writing to a plain
SQLite file. This slice runs the same two paths with the production
registration (tool_registry_memory.register), the real remember handler
and the suite's isolated Cortex SQLite store. The only double is a gate
on the store's insert_memory_exact: the handler has run its write gate
and curation and blocks right before the row is written.

Both tests pin that the worker keeps the remember admission slot until
it ends, abandoned_work records it completed, and the memory row lands
with the sent content after the client stopped listening, with nothing
written before. Verified red against a mutant that releases the slot at
the cancel (both tests fail: semaphore value 4 for budget 4), green on
main five runs in a row.

Refs #728

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018kyZYsEz45oWvFafZFRXK1
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-07T09:39:23.673030Z a514fcb New commits
🔒 Security Review ✅ Completed 2026-11-07T09:40:18.836322Z a514fcb New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Claude finished @cdeust's task in 2m 39s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Nov 7, 2026

Copy link
Copy Markdown

Review 1 of 3 (ADR-1119): PR #943

Five reviews of the diff origin/main...4ef429d: code quality, test coverage, documentation accuracy, security, performance. No Blockers.

  • The gate fixture patches the same store instance that remember._get_store() uses (same get_shared_store(DB_PATH, EMBEDDING_DIM) key, after the conftest singleton reset). The patched insert_memory_exact is the real write: remember_helpers.py:683 → memory_exact.insert_exact.
  • The claimed mutant (sem.release() in the CancelledError branch of run_admitted) fails _assert_abandoned_and_held every time, in both tests.
  • Isolation holds: tests_py/conftest.py redirects CORTEX_MEMORY_DB_PATH (and DATABASE_URL for PostgreSQL) before any mcp_server import. The diff contains no secrets or PII.
  • Every wait is bounded by _GUARD, and the suite's timeout = 300 caps the worst failure path (about 120 to 150 s), so a failure cannot hang CI. The craftsmanship limits hold (181 lines, every function under 40 lines, # source: on the numeric constants).
  • Not checked here: the CI result on this commit (gh pr checks was not permitted in this run), and the test was read, not run.

Notes, no action before merge

  • The docstring says the handler "has run its write gate, embedding and curation", but with force=True the gate is bypassed (determine_bypass) and try_curation returns create at once (remember_helpers.py:448-449).
  • The docstring and CHANGELOG say "isolated Cortex SQLite store", but the CI unit job runs with CORTEX_MEMORY_STORE_BACKEND=postgresql. The test holds on both backends, so "the suite's isolated Cortex store" would be accurate.
  • If a test fails, the gate teardown releases the worker without waiting for it, so a late insert can race the conftest table purge. Only an already-red run is affected; waiting on an ended event in teardown would close it.

Verdict: approve

cdeust commented Nov 7, 2026

Copy link
Copy Markdown
Owner Author

The three notes from review 1 are addressed in fixup b825723:

  • Docstring: with force=True, the write gate and curation are bypassed (try_curation returns create). The docstring now says this. The handler has embedded the content and built the record when it blocks.
  • "Isolated Cortex store": SQLite locally, PostgreSQL in the CI unit job (scripts/ci_unit.sh:58). The docstring and CHANGELOG say so.
  • Teardown: _GatedInsert sets done in a finally. If the insert was reached, the fixture waits for it (bounded by _GUARD) before the conftest purge.

Local run: 2 passed. ruff 0.17.3 and craftsmanship: OK. CI not yet observed on b825723.


Generated by Claude Code

@claude

claude Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Claude finished @cdeust's task in 1m 50s —— View job


Review 2 of 3 (ADR-1119): PR #943

No Blockers. The fixup narrows the teardown race through a done event set in a finally, with bounded waits. It does not weaken any assertion, and the new docstring and CHANGELOG wording is accurate. The posted review has 3 Notes, none needing action before merge.

Not checked: CI on b825723, and the tests were read, not run.

Verdict: approve

@claude

claude Bot commented Nov 7, 2026

Copy link
Copy Markdown

Review 2 of 3 (ADR-1119): PR #943

Review 1 (4ef429d) found no Blockers. This review covers only the commit pushed since then, b825723 (fixup). I ran five reviews on it: code quality, test coverage, documentation accuracy, security and performance. No Blockers.

What the fixup changes

  • The teardown race from review 1 is narrowed. done is set in a finally, so it fires on every path: success, the release assert timing out, or the real insert raising. Teardown sets release before it waits on done, and both waits are bounded by _GUARD.
    • Passing runs get no extra delay: done is already set before teardown.
    • The worst failing run still finishes well under the timeout = 300 limit.
  • The assertions are unchanged. The test still pins all four properties: the slot is held, abandoned_work counts the worker and then records it completed, nothing is written before the release, and the row lands after it.
  • The new docstring and CHANGELOG wording is accurate:
    • CI unit runs on PostgreSQL through scripts/ci_unit.sh.
    • force=True bypasses the write-gate decision (determine_bypass) and try_curation.
    • The handler embeds the content and builds the record before calling insert_memory_exact.
  • Security: no secrets or PII, and no writes outside the isolated store.
  • Not checked: CI on b825723, and the tests were read, not run.

Notes, no action before merge

  • After a failed test, the late-write race is only partly closed. Teardown skips the wait when reached is unset. done also fires before the post-store writes (_run_post_store, mood EMA, wiki sync). The ignored done.wait(_GUARD) result times out silently. Waiting for abandoned_work.snapshot()["in_flight"] to drain would cover every case.
  • The docstring says "tests_py/conftest.py redirects both before any import". That holds for SQLite, but DATABASE_URL is set at conftest.py:144, after _composition_root_wiring has been imported. It is harmless because ci_unit.sh exports the same URL and settings are read lazily, but "before any store is opened" would be exact.
  • One reflowed line in CHANGELOG.md (line 92) is now about 130 columns; the surrounding lines wrap at about 80.

Verdict: approve

cdeust commented Nov 7, 2026

Copy link
Copy Markdown
Owner Author

Review 2 notes:

  • CHANGELOG line 92 (142 columns, introduced by b825723): rewrapped in fixup a514fcb.
  • "redirects both before any import": now "points both at throwaway stores before any store is opened" (a514fcb).
  • Teardown after a failed test: kept as is. The fixture is synchronous and runs after the client's loop has closed, so it cannot wait for abandoned_work to drain; that state is updated by callbacks on that loop. The remaining race only affects a run that is already red. A stronger guard would need an async fixture tied to the test's loop, which is not worth the code for a failure-only path.

Local: 2 passed; ruff 0.17.3 OK. CI not yet observed on a514fcb.


Generated by Claude Code

@claude

claude Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Claude finished @cdeust's task in 1m 30s —— View job


Review 3 of 3 (ADR-1119): PR #943

No Blockers. The fixup changes only the module docstring of the test file and rewraps one CHANGELOG line. No test logic or assertion changed. The new wording, "before any store is opened", matches tests_py/conftest.py (:62 and :144 run before the first connection at :178-181).

Accepted: the whole PR (CHANGELOG.md and tests_py/server/test_mcp_protocol_cancellation_remember.py, all three commits). No commit has an open Blocker, so nothing goes to a refinement PR.

The posted review has 2 Notes, neither needing action before merge.

Not checked: CI on a514fcb. The tests were not re-run.

Verdict: approve

@claude

claude Bot commented Nov 7, 2026

Copy link
Copy Markdown

Review 3 of 3 (ADR-1119): PR #943

Review 2 (b825723) found no Blockers. Since then one commit has been pushed, a514fcb (fixup). Only that commit was reviewed, through five reviews: code quality, test coverage, documentation accuracy, security and performance. No Blockers.

  • Scope: I checked git diff b825723 a514fcb. It changes only the module docstring of tests_py/server/test_mcp_protocol_cancellation_remember.py (lines 1-15) and rewraps one paragraph in CHANGELOG.md, which fixes the ~130-column line from review 2. No test logic, assertion, constant or fixture changed.
  • New wording: "points both at throwaway stores before any store is opened". In tests_py/conftest.py, CORTEX_MEMORY_DB_PATH is set at :62 and DATABASE_URL at :144, both when conftest is imported. The first connection is opened later, at :178-181, which is after both. The wording therefore answers review 2's ordering note.
  • Security and performance: no secrets or PII. The change is documentation only, with no runtime cost.
  • Not checked: CI on a514fcb. The tests were not re-run, which matters little for a docstring-only commit.

Accepted: the whole PR (4ef429d, b825723, a514fcb: CHANGELOG.md and tests_py/server/test_mcp_protocol_cancellation_remember.py). No file or commit has an open Blocker, so nothing needs a refinement PR.

Notes, no action before merge

  • "Throwaway" is literal for SQLite, which gets a mkdtemp tree. For PostgreSQL the database is throwaway only because scripts/ci_unit.sh creates a fresh cluster, or because conftest creates a per-process database (conftest.py:138-142, which can be skipped with CORTEX_TEST_ISOLATE_DB=0 or CORTEX_TEST_DATABASE_URL).
  • The source: quote at lines 25-26 still says "isolated Cortex SQLite store", while the docstring now says the CI store is PostgreSQL. It is accurate as a quote of the issue, but a reader may see the two as contradicting each other.

Verdict: approve

@github-actions
github-actions Bot merged commit 0af0dbd into main Nov 7, 2026
18 checks passed
github-actions Bot pushed a commit that referenced this pull request Nov 7, 2026
…pletion (#728, slice 5) (#946)

* docs(admission): ADR-1125, a cancelled tool call's worker runs to completion (#728, slice 5)

Issue #728 asks to keep work counted "until completion or a documented
cooperative cancellation boundary". Slices 1-4 (#885, #903, #902, #936,
#943) implement and prove the first: the slot is released by the worker's
done callback, the caller awaits through asyncio.shield, abandoned workers
are reported in memory_stats.abandoned_workers. Nothing forwards the
cancellation into the worker thread, so no cooperative boundary exists.

ADR-1125 records those semantics, amends ADR-0329 (the slot belongs to the
worker), states what a handler that wants to stop early must specify, and
lists the tests that hold each point. Mirror regenerated with
wiki_decision_mirror.generate_mirror.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018kyZYs8nydviS39BjwT88E

* fixup! docs(admission): ADR-1125, a cancelled tool call's worker runs to completion (#728, slice 5)

---------

Co-authored-by: Claude <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants