Skip to content

Release/v2.36.0 - #128

Merged
Lyellr88 merged 7 commits into
MARM-mainfrom
release/v2.36.0
Aug 2, 2026
Merged

Release/v2.36.0#128
Lyellr88 merged 7 commits into
MARM-mainfrom
release/v2.36.0

Conversation

@Lyellr88

@Lyellr88 Lyellr88 commented Aug 2, 2026

Copy link
Copy Markdown
Owner

v2.36.0: Automatic Concept Graph Indexing

The concept graph builds itself now. Storing a memory queues it, and a background worker turns it into a node about 30 seconds later on both transports, so the graph stops being stale until someone remembers to run a build.

  • New memories reach the graph with no tool call. marm_concept_build remains for rebuilds and for indexing memories written before this release. CONCEPT_AUTO_INDEX=false turns automation off.
  • Indexing runs on a durable outbox written in the same transaction as the memory, so a memory cannot exist without its indexing task and a process killed mid-extraction loses nothing. Nothing on this path can block or slow a write, and shutdown stops the worker rather than waiting on extraction.
  • Fixed: builds truncated at the 500 newest rows, leaving older memories permanently unreachable on any larger corpus. Builds now page through the whole scope; CONCEPT_BUILD_ROW_CAP is a page size, not a ceiling.
  • Changed: compacted sessions index their source memories instead of the generated summary, so concepts are attributed to where they were actually stated rather than to a paraphrase. This is also what makes a memory graph-eligible the moment it is written.
  • Running HTTP and STDIO together is safe. Both take a leased lock in the memory database before touching the graph, renewed while work runs, so a rebuild cannot drop graph tables while the other process is writing to them. A build that finds the graph busy reports build_in_progress.
  • Measured on a real 768-memory corpus with the worker draining a full backlog (scripts/benchmarking/performance/bench_concept_worker.py --from-live, two runs): recall median moves from 8.3-8.5ms to 15.0-17.7ms, writes are unaffected. The cause is CPU contention from entity extraction, not lock waiting. It applies only while a backlog is draining, not in steady state.
  • 1,023 tests, including two-process coverage that runs a real second interpreter against one database to exercise the lease and the graph lock.

Upgrade Note

This release requires one graph rebuild. Existing graphs hold entities extracted from compaction summaries that the new rule would never produce, and there is no way to remove only those. MARM reports rebuild_required until you run:

marm_concept_build(search_all=True)

The old graph is backed up beside the database first. The build clears the queue it covered, so the worker does not immediately re-extract the same corpus.

Summary by CodeRabbit

  • New Features

    • Added automatic background indexing for the concept graph as new memories are created.
    • Added durable retries, batching, configurable worker settings, and safe coordination across processes.
    • Explorer graph data now refreshes automatically when indexing updates are available.
    • Added queue status visibility and expanded concept-building workflows, including full-scope paginated builds.
  • Bug Fixes

    • Improved handling of deleted, changed, compacted, or failed memory indexing.
    • Added safeguards for stale graphs and interrupted builds.
  • Documentation

    • Updated installation guides, configuration references, troubleshooting guidance, and upgrade instructions for version 2.36.0.

The concept graph only grew when someone ran a build, so it was stale
until a human remembered to refresh it. Storing a memory now queues it
and a background worker turns it into a node about 30 seconds later, on
both transports.

Automation:
- Durable outbox in the memory DB, enqueued in the same transaction as
  the memory, so a memory cannot exist without its indexing task and a
  killed process loses nothing. Teardown stops the worker rather than
  waiting on extraction.
- Per-memory build outcomes (indexed/no_entities/failed/vanished). The
  aggregate counters cannot report a failed extraction, and settling
  queue tasks on them would delete work promised for retry.
- Failures retry with backoff and park after CONCEPT_INDEX_MAX_ATTEMPTS
  rather than blocking the queue. Nothing here can block a write.
- Console Explorer polls a cheap change marker and refreshes the graph
  without a reload, stopping when its tab is not showing.

Fixes:
- Builds truncated at the 500 newest rows, leaving older memories
  permanently unreachable. They now page through the whole scope;
  CONCEPT_BUILD_ROW_CAP is a page size, not a ceiling.
- Builds indexed compaction summaries instead of their sources, so
  concepts were attributed to a paraphrase. Inverted, which is also
  what makes a memory graph-eligible the moment it is written.
- init_concept_database stamped the current schema version on every
  ConceptDB construction, so one console delete could mark a stale
  graph as current and skip its rebuild.
- The Console restated the schema version as a literal and would have
  called every rebuilt graph stale after the bump.

Concurrency:
- HTTP and STDIO are two processes over one database and the build lock
  was an asyncio.Lock. A rebuild drops the graph tables, so both writers
  now take a leased row in the memory DB, renewed while work runs and
  always acquired before the in-process lock.
- Losing that lease stops the build at the next memory and settles
  nothing, rather than writing alongside the new owner.

BREAKING CHANGE: existing graphs report rebuild_required and need one
marm_concept_build(search_all=True). They hold summary-derived entities
the new rule would never produce and there is no selective retraction.
The old graph is backed up beside the database first.
The suite disables the encoder for isolation, so nothing exercised store
and recall latency while the background indexer runs. Adds the benchmark
that does, and acts on what it found.

Measured on a 400-memory corpus, worker draining a full backlog:
- recall  12ms -> 20ms median, p95 18ms -> 33ms
- writes  11ms -> 19ms median

The spec named the encoder lock as the main risk. Half right. Disabling
entity-name embedding drops the write cost from +65% to +13% and leaves
recall unchanged at +82%, so recall contention is CPU competition from
spaCy extraction, not lock waiting.

- New CONCEPT_INDEX_BATCH_PAUSE_MS (250) pauses between batches. It does
  not move p95 (32.6 -> 32.0ms) and costs 18% drain time, but it cuts
  worst-case recall from ~270ms to ~80ms, reproducibly across runs. The
  tail is what a user feels, so it is on by default; 0 disables it.
- Smaller batches were tried and are worse: batch 5 with the same pause
  took p95 to 100ms, so batch size stays at 20.
- bench_concept_worker.py runs the two-phase comparison or a pause sweep,
  against a synthetic corpus or a copy of the live one.
Running the benchmark with --from-live exposed a bug in the benchmark
itself: it enqueued COALESCE(content_hash, id), so every memory whose
content_hash column was NULL got a hash that could never match. The
worker extracted all of them, saw a mismatch, and discarded every result
as superseded. It paid the CPU cost and wrote nothing, and the run
reported timings for a worker that never indexed anything.

- Backfill the real hash when queueing legacy rows.
- Count progress against the ids actually queued. Probe writes enqueue
  themselves, so raw queue depth grew while the worker was clearing it
  and the "still pending" line was meaningless.
- Guard the worker against a NULL stored hash. Treating it as a mismatch
  never settles the task and never counts a failure, so that memory is
  re-extracted forever without ever being written or parked. Unreachable
  from the shipped write paths, which always compute a hash, but it cost
  a whole benchmark run to notice.

Corrected numbers, real 768-memory corpus, two runs:
- store_memory   10.2-11.2ms -> 10.8-11.1ms   (-0.5% to +6%)
- recall_similar  8.3-8.5ms  -> 15.0-17.7ms   (+80% to +109%)

The published write regression was an artifact of short synthetic
memories generating entity embeddings much faster than real content
does. Writes are effectively unaffected on a real corpus. README section
5 now records this, and the benchmarking README says to prefer
--from-live before publishing.
The dedicated benchmarks section for concept indexing was more detail
than the cost warranted. Removing it left four references pointing at a
heading that no longer exists, plus a gap in the section numbering.

- Renumber LoCoMo and the competitor matrix back to 5 and 6, and restore
  the intro sentence that described what each section covers.
- Point the three README bullets at the benchmark script instead of the
  removed anchor. The measured numbers stay where a reader meets the
  feature; only the deep-dive is gone.
- Drop the same anchor from the CHANGELOG and the benchmarking README.

No code changes.
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Automatic concept-graph indexing now uses a durable SQLite queue, retries, leases, cross-process graph locking, background workers, paginated builds, and graph-version polling for Explorer refreshes. Release metadata, documentation, lifecycle wiring, tests, and benchmarking were updated for v2.36.0.

Changes

Concept indexing

Layer / File(s) Summary
Queue, locking, and worker processing
marm-mcp-server/marm_mcp_server/core/...
Memory changes enqueue concept tasks transactionally. The queue supports leases, retries, parking, and ownership checks. The worker processes batches under a cross-process graph lease.
Paginated and incremental builds
marm-mcp-server/marm_mcp_server/endpoints/concepts.py, marm-mcp-server/marm_mcp_server/core/concept_db.py
Builds use keyset pagination, exclude compaction summaries, support targeted IDs, report outcomes, handle lease loss, and retire settled queue tasks.
Configuration and lifecycle wiring
marm-mcp-server/marm_mcp_server/config/settings.py, marm-mcp-server/marm_mcp_server/server*.py, marm-mcp-server/marm_mcp_server/core/shutdown_manager.py
Worker settings support enablement, debounce, batch limits, pauses, leases, and retry caps. HTTP and STDIO lifecycles start and stop the worker. Runtime status reports queue state.
Graph version and Explorer refresh
marm-mcp-server/marm_mcp_server/console/..., marm-console/artifacts/marm-console/src/...
The Console API exposes a graph version marker. Explorer polling invalidates graph-related queries when the marker changes.
Validation and benchmarking
marm-mcp-server/tests/test_concept_*.py, marm-mcp-server/tests/test_console_graph_version.py, scripts/benchmarking/...
Tests cover queue behavior, concurrency, pagination, worker races, lifecycle handling, schema state, and graph-version states. A worker contention benchmark measures write and recall latency.

Release documentation

Layer / File(s) Summary
v2.36.0 metadata and documentation
CHANGELOG.md, README.md, marm-mcp-server/README.md, marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md, docs/INSTALL-*, marm-mcp-server/{Dockerfile,pyproject.toml,server.json,docker-compose.yml}
Release versions were updated to 2.36.0. Documentation describes automatic indexing, rebuilds, queue operations, configuration, troubleshooting, and benchmark usage.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4ca0f8fc1

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +82 to +84
task.cancel()
try:
await task

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the graph lock until extraction actually stops

When shutdown occurs while _process is awaiting build_for_memory_ids, this cancellation only cancels the await around asyncio.to_thread; the _run_build thread continues writing. Unwinding _drain then stops both heartbeats and releases the cross-process graph lock, allowing the other transport to acquire it and reset or write the same concept database concurrently. Signal the abort event and retain the lock until the extraction thread observes it or completes.

Useful? React with 👍 / 👎.

Comment on lines 360 to +363
conn.execute("DELETE FROM memory_chunks WHERE memory_id = ?", (memory_id,))
# A console edit replaces the content wholesale, so the graph holds
# entities from text that no longer exists until this is re-indexed.
enqueue_concept_index(conn, memory_id, content_hash)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Retract obsolete provenance before reindexing replacements

When an existing memory is replaced through the Console, this queues its new content, but the targeted build only adds entities and relationships and never removes the memory ID from concepts derived from the previous content. The same issue affects merges and document resaves, so after the task completes concept recall can permanently return facts that are no longer present in the memory. Remove the old provenance under the graph build lock before extracting the replacement.

Useful? React with 👍 / 👎.

Comment on lines +168 to +173
if "entities" not in existing_tables:
conn.execute(
"INSERT INTO concept_schema_metadata (key, value) VALUES (?, ?) "
"ON CONFLICT(key) DO NOTHING",
(_SCHEMA_VERSION_KEY, str(CONCEPT_SCHEMA_VERSION)),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark schema v3 current only after the rebuild completes

During the required v2-to-v3 upgrade, _prepare_build_schema drops the old graph before calling this initializer, so existing_tables lacks entities and these lines stamp schema version 3 before any corpus rows are rebuilt. If the process exits, loses its lock, or raises during the subsequent build, inspect_concept_schema reports current after restart and no longer prompts for the required rebuild; legacy memories also have no automatic-index queue rows to recover the missing portion. Preserve a rebuild-required marker until the full search_all build succeeds.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
marm-mcp-server/tests/test_concept_endpoints.py (1)

285-316: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Align the pagination baseline with _BUILD_ROW_FILTERS.

_BUILD_ROW_FILTERS keeps rows when compaction_role is NULL or not "summary", while marm-mcp-server/tests/test_concept_build_pagination.py:96-99 hand-makes (compaction_role IS NULL OR compaction_role != 'source'). Update the baseline SQL to match the production filter.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@marm-mcp-server/tests/test_concept_endpoints.py` around lines 285 - 316, The
test setup for
test_fetch_memory_pages_indexes_sources_and_skips_compaction_summaries uses the
wrong compaction-role condition. Update the hand-built baseline SQL in the
related pagination test to retain rows where compaction_role is NULL or not
'summary', matching _BUILD_ROW_FILTERS, rather than excluding 'source'.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@marm-mcp-server/marm_mcp_server/core/concept_build_lock.py`:
- Around line 168-184: Update the _keep_alive heartbeat to track the timestamp
of the last successful renewal and set lease.lost, log the lock loss, and stop
looping once the elapsed time since that success exceeds ttl_seconds, including
when renew raises exceptions. Preserve the existing immediate lease.lost
behavior when renew returns False, while resetting the successful-renewal
timestamp after each renewal.

In `@marm-mcp-server/README.md`:
- Line 161: Restore the missing Section 5 benchmark results, or remove its
reference, consistently in both marm-mcp-server/README.md (line 161) and
marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md (line 135). Ensure
the subsequent headings remain correctly numbered: retain Section 6 at lines 205
and 179 and Section 7 at lines 217 and 191 only when their preceding sections
exist; otherwise renumber them sequentially.

In `@marm-mcp-server/tests/test_concept_two_process.py`:
- Around line 188-222: Make test_work_outliving_its_ttl_keeps_both_locks
reliable by preventing the synchronous _run_in_second_process call from blocking
the event loop: execute the child-process invocation via asyncio.to_thread while
preserving its existing assertions and lock/claim scenario. Keep the TTL
behavior under test unchanged.

In `@marm-mcp-server/tests/test_concept_worker_wiring.py`:
- Around line 17-31: Update _reload_settings and its callers to require a pytest
tmp_path fixture and set MARM_DB_PATH and MARM_API_KEY to paths under that
temporary directory before reloading marm_mcp_server.config.settings. Ensure
both the initial reload and restore_settings reload use isolated test paths,
preventing DEFAULT_DB_PATH or the API key path from touching ~/.marm or real
parent directories.

In `@marm-mcp-server/tests/test_graph_context.py`:
- Around line 236-241: Strengthen the test around
memory_endpoints._cleanup_deleted_concepts by asserting the returned status
equals the cleanup success value rather than merely not being failed. Ensure the
test creates or uses the concept database so cleanup is not skipped, then verify
the provenance data for concept “m1” has been removed.

In `@README.md`:
- Line 912: Update the README section describing concept indexing to qualify the
durable outbox and background-worker behavior as automatic mode, while stating
that CONCEPT_AUTO_INDEX=false still creates concept_index_queue rows. Update the
corresponding CHANGELOG.md entry at lines 8-10 to clarify that manual mode
disables processing but does not stop queued work.

In `@scripts/benchmarking/performance/bench_concept_worker.py`:
- Around line 388-414: Update the contended benchmark flow around the warm-up
loop and still_working calculation to track whether queued work actually
decreased before timing. When no memories are indexed, report the worker as
idle/no-progress rather than “busy throughout,” and ensure the contention
measurements are not presented as valid busy-worker results; preserve the
existing reporting for workers that drain at least one memory.

In `@scripts/benchmarking/README.md`:
- Around line 45-47: Update the benchmark description near the “worker stopped”
and “drains a queue” comparison to limit the full-corpus backlog claim to
upgrades with an existing corpus; do not state that every installation enters
this state, while preserving the existing explanation of the upgrade rebuild
path.
- Around line 54-55: Update the --from-live benchmark setup to create a
consistent SQLite snapshot before copying marm_memory.db and its sidecars, using
sqlite3.backup semantics or explicitly requiring the database to be quiescent
during the copy; preserve the guarantee that the original database is never
modified.

---

Outside diff comments:
In `@marm-mcp-server/tests/test_concept_endpoints.py`:
- Around line 285-316: The test setup for
test_fetch_memory_pages_indexes_sources_and_skips_compaction_summaries uses the
wrong compaction-role condition. Update the hand-built baseline SQL in the
related pagination test to retain rows where compaction_role is NULL or not
'summary', matching _BUILD_ROW_FILTERS, rather than excluding 'source'.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9cee15d8-0f88-40b4-aee4-a965735cdccb

📥 Commits

Reviewing files that changed from the base of the PR and between cccb770 and e4ca0f8.

📒 Files selected for processing (45)
  • CHANGELOG.md
  • CONTRIBUTORS.md
  • README.md
  • RELEASE-NOTES-v2.35.0.md
  • docs/INSTALL-DOCKER.md
  • docs/INSTALL-LINUX.md
  • docs/INSTALL-PLATFORMS.md
  • docs/INSTALL-WINDOWS.md
  • marm-console/artifacts/marm-console/src/components/knowledge/ExplorerTab.tsx
  • marm-console/artifacts/marm-console/src/hooks/use-marm-queries.ts
  • marm-console/artifacts/marm-console/src/lib/marm-api.ts
  • marm-console/artifacts/marm-console/src/lib/marm-types.ts
  • marm-mcp-server/Dockerfile
  • marm-mcp-server/README.md
  • marm-mcp-server/docker-compose.yml
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/marm_mcp_server/console/concept_store.py
  • marm-mcp-server/marm_mcp_server/console/endpoints/concepts.py
  • marm-mcp-server/marm_mcp_server/core/concept_build_lock.py
  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/core/concept_queue.py
  • marm-mcp-server/marm_mcp_server/core/concept_worker.py
  • marm-mcp-server/marm_mcp_server/core/memory_db.py
  • marm-mcp-server/marm_mcp_server/core/memory_delete.py
  • marm-mcp-server/marm_mcp_server/core/memory_ops.py
  • marm-mcp-server/marm_mcp_server/core/shutdown_manager.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
  • marm-mcp-server/marm_mcp_server/server.py
  • marm-mcp-server/marm_mcp_server/server_stdio.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • marm-mcp-server/pyproject.toml
  • marm-mcp-server/server.json
  • marm-mcp-server/tests/test_concept_build_pagination.py
  • marm-mcp-server/tests/test_concept_endpoints.py
  • marm-mcp-server/tests/test_concept_incremental_build.py
  • marm-mcp-server/tests/test_concept_queue.py
  • marm-mcp-server/tests/test_concept_two_process.py
  • marm-mcp-server/tests/test_concept_worker.py
  • marm-mcp-server/tests/test_concept_worker_wiring.py
  • marm-mcp-server/tests/test_console_graph_version.py
  • marm-mcp-server/tests/test_graph_context.py
  • scripts/benchmarking/README.md
  • scripts/benchmarking/performance/bench_concept_worker.py
💤 Files with no reviewable changes (1)
  • RELEASE-NOTES-v2.35.0.md
📜 Review details
🧰 Additional context used
📓 Path-based instructions (14)
{README.md,marm-mcp-server/README.md,marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md,docs/INSTALL-*.md}

📄 CodeRabbit inference engine (AGENTS.md)

When bumping the version, update the README h1 in all three README variants and the version headers in installation documents.

Files:

  • docs/INSTALL-DOCKER.md
  • docs/INSTALL-LINUX.md
  • docs/INSTALL-WINDOWS.md
  • docs/INSTALL-PLATFORMS.md
  • marm-mcp-server/README.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
  • README.md
**/*.{py,md,json,toml,yml,yaml,Dockerfile}

📄 CodeRabbit inference engine (AGENTS.md)

Use SemVer: MAJOR for breaking changes, MINOR for new tools, parameters, or features, and PATCH for fixes and documentation updates.

Files:

  • docs/INSTALL-DOCKER.md
  • marm-mcp-server/pyproject.toml
  • docs/INSTALL-LINUX.md
  • docs/INSTALL-WINDOWS.md
  • marm-mcp-server/server.json
  • marm-mcp-server/marm_mcp_server/console/endpoints/concepts.py
  • marm-mcp-server/docker-compose.yml
  • marm-mcp-server/marm_mcp_server/core/memory_delete.py
  • marm-mcp-server/tests/test_concept_two_process.py
  • marm-mcp-server/tests/test_concept_build_pagination.py
  • marm-mcp-server/tests/test_console_graph_version.py
  • marm-mcp-server/marm_mcp_server/core/memory_db.py
  • CONTRIBUTORS.md
  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • CHANGELOG.md
  • marm-mcp-server/tests/test_graph_context.py
  • marm-mcp-server/marm_mcp_server/core/memory_ops.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • marm-mcp-server/marm_mcp_server/core/shutdown_manager.py
  • marm-mcp-server/marm_mcp_server/server_stdio.py
  • marm-mcp-server/marm_mcp_server/server.py
  • scripts/benchmarking/README.md
  • docs/INSTALL-PLATFORMS.md
  • marm-mcp-server/marm_mcp_server/console/concept_store.py
  • marm-mcp-server/tests/test_concept_incremental_build.py
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/tests/test_concept_queue.py
  • marm-mcp-server/tests/test_concept_worker_wiring.py
  • marm-mcp-server/marm_mcp_server/core/concept_queue.py
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/marm_mcp_server/core/concept_build_lock.py
  • marm-mcp-server/marm_mcp_server/core/concept_worker.py
  • marm-mcp-server/tests/test_concept_worker.py
  • marm-mcp-server/tests/test_concept_endpoints.py
  • marm-mcp-server/README.md
  • scripts/benchmarking/performance/bench_concept_worker.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
  • README.md
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Only flag documentation issues that are materially wrong, misleading for installation/release behavior, or inconsistent with live MCP behavior. Skip style, phrasing, formatting, and wording preferences.

Files:

  • docs/INSTALL-DOCKER.md
  • docs/INSTALL-LINUX.md
  • docs/INSTALL-WINDOWS.md
  • CONTRIBUTORS.md
  • CHANGELOG.md
  • scripts/benchmarking/README.md
  • docs/INSTALL-PLATFORMS.md
  • marm-mcp-server/README.md
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
  • README.md
marm-mcp-server/{pyproject.toml,server.json,Dockerfile,docker-compose.yml}

📄 CodeRabbit inference engine (AGENTS.md)

When bumping the version, update the package version, all three server.json occurrences, the Docker version label and compose configuration.

Files:

  • marm-mcp-server/pyproject.toml
  • marm-mcp-server/server.json
  • marm-mcp-server/docker-compose.yml
  • marm-mcp-server/Dockerfile
marm-mcp-server/{server.py,server_stdio.py,server.json}

📄 CodeRabbit inference engine (AGENTS.md)

When adding or removing an MCP tool, update the endpoint implementation, HTTP route and whitelist, STDIO wrapper or service registration, and the server.json tools array.

Files:

  • marm-mcp-server/server.json
marm-mcp-server/marm_mcp_server/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

marm-mcp-server/marm_mcp_server/**/*.py: Keep changes surgical, touch only what the task requires, match existing style, and preserve behavior during refactors.
All memory writes must use the serialized asynchronous write queue and must not bypass its single worker.
marm_log_entry must dual-write a log_entries row and semantic memory, but semantic-store failure must never fail the log write.
Graph and concept failures must never break the seven core memory tools; graph-aware recall must keep primary memory ranking authoritative and treat graph enrichment as bounded, read-only, and fail-open.
Keep the memory and concept graph SQLite databases isolated and never share connections between their pools.
Writes must succeed when the embedding encoder is unavailable; use the single lazy-loaded 512-dimensional jinaai/jina-embeddings-v2-small-en encoder behind a lock.
Keep orchestration in its current owner file and extract modules only at real boundaries; avoid speculative abstractions and unnecessary configuration flags.
Use minimal comments only for non-obvious reasons; never add comments that narrate the next line.

Files:

  • marm-mcp-server/marm_mcp_server/console/endpoints/concepts.py
  • marm-mcp-server/marm_mcp_server/core/memory_delete.py
  • marm-mcp-server/marm_mcp_server/core/memory_db.py
  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/core/memory_ops.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • marm-mcp-server/marm_mcp_server/core/shutdown_manager.py
  • marm-mcp-server/marm_mcp_server/server_stdio.py
  • marm-mcp-server/marm_mcp_server/server.py
  • marm-mcp-server/marm_mcp_server/console/concept_store.py
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/marm_mcp_server/core/concept_queue.py
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/marm_mcp_server/core/concept_build_lock.py
  • marm-mcp-server/marm_mcp_server/core/concept_worker.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
**/*.py

⚙️ CodeRabbit configuration file

**/*.py: Prioritize runtime correctness, async/concurrency safety, SQLite transaction safety, auth/rate-limit behavior, release-breaking packaging issues, and MCP protocol compatibility.

Files:

  • marm-mcp-server/marm_mcp_server/console/endpoints/concepts.py
  • marm-mcp-server/marm_mcp_server/core/memory_delete.py
  • marm-mcp-server/tests/test_concept_two_process.py
  • marm-mcp-server/tests/test_concept_build_pagination.py
  • marm-mcp-server/tests/test_console_graph_version.py
  • marm-mcp-server/marm_mcp_server/core/memory_db.py
  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/tests/test_graph_context.py
  • marm-mcp-server/marm_mcp_server/core/memory_ops.py
  • marm-mcp-server/marm_mcp_server/services/runtime_status.py
  • marm-mcp-server/marm_mcp_server/core/shutdown_manager.py
  • marm-mcp-server/marm_mcp_server/server_stdio.py
  • marm-mcp-server/marm_mcp_server/server.py
  • marm-mcp-server/marm_mcp_server/console/concept_store.py
  • marm-mcp-server/tests/test_concept_incremental_build.py
  • marm-mcp-server/marm_mcp_server/__init__.py
  • marm-mcp-server/tests/test_concept_queue.py
  • marm-mcp-server/tests/test_concept_worker_wiring.py
  • marm-mcp-server/marm_mcp_server/core/concept_queue.py
  • marm-mcp-server/marm_mcp_server/config/settings.py
  • marm-mcp-server/marm_mcp_server/core/concept_build_lock.py
  • marm-mcp-server/marm_mcp_server/core/concept_worker.py
  • marm-mcp-server/tests/test_concept_worker.py
  • marm-mcp-server/tests/test_concept_endpoints.py
  • scripts/benchmarking/performance/bench_concept_worker.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
marm-mcp-server/tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

marm-mcp-server/tests/**/*.py: Run tests with pytest from marm-mcp-server/; use real FastAPI endpoints and real SQLite, mocking only when it meaningfully speeds testing and matches real behavior with at least 95% fidelity.
Every new MARM Console API route must have at least one happy-path FastAPI response-contract test with the MCP adapter stubbed.
Do not write existence-check or coded-to-pass tests; prefer deep tests exercising real paths, and use pytest.mark.skip only for genuinely unavailable dependencies.

Files:

  • marm-mcp-server/tests/test_concept_two_process.py
  • marm-mcp-server/tests/test_concept_build_pagination.py
  • marm-mcp-server/tests/test_console_graph_version.py
  • marm-mcp-server/tests/test_graph_context.py
  • marm-mcp-server/tests/test_concept_incremental_build.py
  • marm-mcp-server/tests/test_concept_queue.py
  • marm-mcp-server/tests/test_concept_worker_wiring.py
  • marm-mcp-server/tests/test_concept_worker.py
  • marm-mcp-server/tests/test_concept_endpoints.py
**/tests/**

⚙️ CodeRabbit configuration file

**/tests/**: Focus on tests that are flaky, non-isolated, incorrectly asserting behavior, or missing coverage for a changed high-risk path. Skip minor naming, comments, and layout preferences.

Files:

  • marm-mcp-server/tests/test_concept_two_process.py
  • marm-mcp-server/tests/test_concept_build_pagination.py
  • marm-mcp-server/tests/test_console_graph_version.py
  • marm-mcp-server/tests/test_graph_context.py
  • marm-mcp-server/tests/test_concept_incremental_build.py
  • marm-mcp-server/tests/test_concept_queue.py
  • marm-mcp-server/tests/test_concept_worker_wiring.py
  • marm-mcp-server/tests/test_concept_worker.py
  • marm-mcp-server/tests/test_concept_endpoints.py
marm-mcp-server/marm_mcp_server/server_stdio.py

📄 CodeRabbit inference engine (AGENTS.md)

STDIO owns the FastMCP app and core tool wrappers; graph and concept services must be explicitly registered after core tools so tools/list order remains stable. Never fork behavior between transports.

Files:

  • marm-mcp-server/marm_mcp_server/server_stdio.py
marm-mcp-server/marm_mcp_server/server.py

📄 CodeRabbit inference engine (AGENTS.md)

HTTP MCP tools must be registered in the route definitions and MCP_TOOL_OPERATIONS whitelist; unlisted tools do not exist over HTTP.

Files:

  • marm-mcp-server/marm_mcp_server/server.py
marm-mcp-server/marm_mcp_server/{__init__.py,server.py}

📄 CodeRabbit inference engine (AGENTS.md)

When bumping the version, update __version__ and its docstring in __init__.py, plus the server docstring.

Files:

  • marm-mcp-server/marm_mcp_server/server.py
  • marm-mcp-server/marm_mcp_server/__init__.py
marm-mcp-server/marm_mcp_server/endpoints/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep endpoint logic in endpoints/, split by surface, and keep shared helpers in core/. New tools should be implemented in the appropriate endpoint module.

Files:

  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
README.md

📄 CodeRabbit inference engine (AGENTS.md)

The root README.md is the single source of truth for README content.

Files:

  • README.md
🧠 Learnings (1)
📚 Learning: 2026-07-31T08:30:32.056Z
Learnt from: Lyellr88
Repo: Lyellr88/marm-memory PR: 125
File: docs/INSTALL-LINUX.md:323-323
Timestamp: 2026-07-31T08:30:32.056Z
Learning: In the Linux and Windows installation documentation, treat http://localhost:8001 as the primary MARM MCP Server endpoint. Do not validate its response contract against the standalone marm-mcp-server/marm_graph/server.py health route, because marm-graph is embedded in the primary marm_mcp_server service.

Applied to files:

  • docs/INSTALL-LINUX.md
  • docs/INSTALL-WINDOWS.md
🪛 ast-grep (0.45.0)
marm-mcp-server/tests/test_concept_two_process.py

[error] 40-46: Command coming from incoming request
Context: subprocess.run(
[sys.executable, "-c", script],
capture_output=True,
text=True,
timeout=120,
env=env,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

scripts/benchmarking/performance/bench_concept_worker.py

[info] 143-143: use secrets package over random package
Context: random.Random(i)
Note: [CWE-330] Use of Insufficiently Random Values.

(avoid-random-python)

🪛 OpenGrep (1.26.0)
marm-mcp-server/marm_mcp_server/core/concept_queue.py

[ERROR] 90-92: SQL query built via f-string passed to execute()/executemany(). Use parameterized queries with placeholders instead.

(coderabbit.sql-injection.python-fstring-execute)


[ERROR] 130-137: SQL query built via f-string passed to execute()/executemany(). Use parameterized queries with placeholders instead.

(coderabbit.sql-injection.python-fstring-execute)


[ERROR] 323-325: SQL query built via f-string passed to execute()/executemany(). Use parameterized queries with placeholders instead.

(coderabbit.sql-injection.python-fstring-execute)

🔇 Additional comments (51)
marm-console/artifacts/marm-console/src/components/knowledge/ExplorerTab.tsx (1)

2-2: LGTM!

Also applies to: 97-99

marm-console/artifacts/marm-console/src/hooks/use-marm-queries.ts (1)

1-1: LGTM!

Also applies to: 24-24, 297-334

marm-console/artifacts/marm-console/src/lib/marm-api.ts (1)

18-18: LGTM!

Also applies to: 209-210

marm-console/artifacts/marm-console/src/lib/marm-types.ts (1)

294-300: LGTM!

marm-mcp-server/marm_mcp_server/console/concept_store.py (1)

12-17: LGTM!

Also applies to: 61-91

marm-mcp-server/marm_mcp_server/console/endpoints/concepts.py (1)

84-89: LGTM!

marm-mcp-server/tests/test_concept_build_pagination.py (1)

18-147: LGTM!

marm-mcp-server/tests/test_concept_endpoints.py (2)

55-79: LGTM!

Also applies to: 111-123, 319-365, 1022-1025


230-238: 📐 Maintainability & Code Quality

No change needed: marm_concept_build calls _run_build positionally, so this stub name change does not affect the tested FastAPI endpoint path.

			> Likely an incorrect or invalid review comment.
marm-mcp-server/tests/test_concept_incremental_build.py (1)

57-374: LGTM!

marm-mcp-server/tests/test_concept_queue.py (1)

44-316: LGTM!

marm-mcp-server/tests/test_concept_two_process.py (1)

21-53: LGTM!

Also applies to: 79-186, 226-291

marm-mcp-server/tests/test_concept_worker.py (1)

19-521: LGTM!

marm-mcp-server/tests/test_concept_worker_wiring.py (1)

75-233: LGTM!

marm-mcp-server/tests/test_console_graph_version.py (1)

16-107: LGTM!

marm-mcp-server/tests/test_graph_context.py (1)

190-215: LGTM!

scripts/benchmarking/performance/bench_concept_worker.py (1)

27-386: LGTM!

Also applies to: 416-426

CHANGELOG.md (1)

3-7: LGTM!

Also applies to: 11-36

CONTRIBUTORS.md (1)

15-15: LGTM!

README.md (1)

8-8: LGTM!

Also applies to: 89-89, 143-144, 699-702, 868-868, 877-887, 903-903, 982-988, 1065-1081

docs/INSTALL-DOCKER.md (1)

5-5: LGTM!

marm-mcp-server/marm_mcp_server/__init__.py (1)

17-20: LGTM!

marm-mcp-server/pyproject.toml (1)

7-7: LGTM!

marm-mcp-server/server.json (1)

6-6: LGTM!

Also applies to: 20-25

marm-mcp-server/docker-compose.yml (1)

8-8: LGTM!

Also applies to: 21-21

docs/INSTALL-LINUX.md (1)

5-5: LGTM!

Also applies to: 323-323

docs/INSTALL-PLATFORMS.md (1)

1-1: LGTM!

docs/INSTALL-WINDOWS.md (1)

5-5: LGTM!

Also applies to: 297-297

marm-mcp-server/Dockerfile (1)

76-76: LGTM!

marm-mcp-server/README.md (1)

10-10: LGTM!

Also applies to: 91-91, 145-146, 701-702, 704-704, 870-870, 879-889, 905-905, 914-914, 984-990, 1067-1083

marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md (1)

1-1: LGTM!

Also applies to: 65-65, 119-120, 675-676, 678-678, 844-844, 853-863, 879-879, 888-888, 958-964, 1041-1057

scripts/benchmarking/README.md (1)

11-13: LGTM!

Also applies to: 39-43, 49-52, 57-77

marm-mcp-server/marm_mcp_server/config/settings.py (1)

58-84: LGTM!

Also applies to: 209-209, 313-371

marm-mcp-server/marm_mcp_server/core/memory_db.py (1)

381-415: LGTM!

marm-mcp-server/marm_mcp_server/core/concept_queue.py (2)

81-92: LGTM!

Also applies to: 95-143, 315-326


232-281: 🗄️ Data Integrity & Integration

No change needed.

Parked rows are reported in queue status and are recovered automatically by later writes via enqueue; there is no manual retry or build retirement path shown to un-park them.

marm-mcp-server/marm_mcp_server/core/concept_build_lock.py (1)

61-93: LGTM!

Also applies to: 96-141

marm-mcp-server/marm_mcp_server/core/concept_db.py (1)

24-27: LGTM!

Also applies to: 164-173

marm-mcp-server/marm_mcp_server/core/memory_ops.py (1)

15-15: LGTM!

Also applies to: 126-130, 270-274, 361-363, 484-486

marm-mcp-server/marm_mcp_server/endpoints/concepts.py (2)

12-16: LGTM!

Also applies to: 34-39, 55-69, 98-123, 126-173, 195-249, 268-268, 312-312, 338-357, 451-481, 557-561, 598-680


682-710: 🩺 Stability & Availability

No change needed. The worker holds concept_build_lock("auto_index", ...) around build_for_memory_ids, and abort is bound from lease.lost.

marm-mcp-server/marm_mcp_server/core/memory_delete.py (1)

6-7: LGTM!

Also applies to: 173-176

marm-mcp-server/marm_mcp_server/core/concept_worker.py (4)

34-71: LGTM!


91-152: LGTM!


226-263: LGTM!


73-89: 🩺 Stability & Availability

Do not return from concept_worker.stop() until in-flight to_thread work stops using the memory SQLite connection.

to_thread cannot cancel a worker thread once it starts; task.cancel() only cancels the await. stop() still waits for that await, so this is not the orphan-thread issue described. The remaining concern is whether concept_queue helpers close the shared memory.connection_pool row immediately after stop() returns while a settlement thread is still running.

marm-mcp-server/marm_mcp_server/core/shutdown_manager.py (1)

7-7: LGTM!

Also applies to: 71-78

marm-mcp-server/marm_mcp_server/server.py (2)

25-25: LGTM!

Also applies to: 68-68


8-8: 📐 Maintainability & Code Quality

Version metadata is consistent.

marm-mcp-server/marm_mcp_server/server_stdio.py (1)

38-38: LGTM!

Also applies to: 68-68, 84-90

marm-mcp-server/marm_mcp_server/services/runtime_status.py (1)

18-18: LGTM!

Also applies to: 120-141

Comment thread marm-mcp-server/marm_mcp_server/core/concept_build_lock.py
Comment thread marm-mcp-server/README.md Outdated
Comment thread marm-mcp-server/tests/test_concept_two_process.py
Comment thread marm-mcp-server/tests/test_concept_worker_wiring.py Outdated
Comment thread marm-mcp-server/tests/test_graph_context.py
Comment thread README.md
Comment thread scripts/benchmarking/performance/bench_concept_worker.py
Comment thread scripts/benchmarking/README.md Outdated
Comment on lines +54 to +55
`--from-live` copies `~/.marm/marm_memory.db` (and its WAL sidecars) into a temp
directory and measures against a real corpus. It never writes to the original.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="$(fd -t f -a 'bench_concept_worker.py' scripts/benchmarking | head -n1)"
test -n "$file"
rg -n -C 8 'from.live|from-live|copy|backup|wal|WAL|sqlite3' "$file"

Repository: Lyellr88/marm-memory

Length of output: 4350


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="$(fd -t f -a 'bench_concept_worker.py' scripts/benchmarking | head -n1)"
test -n "$file"

echo "## File excerpt around live copy"
sed -n '80,105p' "$file" | cat -n -v

echo
echo "## Search for SQLite backup API and transaction locking related docs/code"
rg -n -C 3 'sqlite3\.backup|Backup|BEGIN IMMEDIATE|BEGIN EXCLUSIVE|BEGIN DEFERRED|LOCKED\s+database|is_locked|db\.locked|lock_mode|busy_timeout' .

Repository: Lyellr88/marm-memory

Length of output: 45878


🌐 Web query:

SQLite WAL mode database locking backup WAL file not copied by operating system file copy during write

💡 Result:

Using operating system file copy commands (such as cp, rsync, or snapshotting) to back up an SQLite database in WAL (Write-Ahead Logging) mode is highly unreliable and generally discouraged because it creates an internally inconsistent database [1][2][3]. In WAL mode, the database state is split across the main database file, the -wal file (containing committed transactions not yet flushed), and the -shm shared-memory file [1][2]. Because these files are updated independently, a naive file copy captures them at different moments [2]. If a write or a checkpoint operation occurs between the copying of the main file and the WAL file, the resulting backup will be corrupted or unusable [4][1][2]. The correct and authoritative ways to back up an SQLite database in WAL mode are: 1. Online Backup API: This is the recommended method [1][2]. SQLite provides an official Online Backup API (available as sqlite3_backup_init/step/finish in C or Connection.backup in languages like Python) that copies the database page-by-page [5][2][6]. It holds a shared read-lock only briefly during each page-copy step and is restart-aware—if a concurrent writer modifies a page that has already been copied, SQLite transparently restarts that part of the process [5][2]. This ensures a consistent point-in-time snapshot without needing to stop database writes [5][6]. 2. Full Checkpoint and Copy: If you must use file-level copying, you must first perform a full checkpoint (e.g., using PRAGMA wal_checkpoint(FULL) or RESTART) to flush all data from the WAL file into the main database file [1][3]. Once the checkpoint is complete and the WAL file is empty/truncated, you can safely copy the main database file [3]. To ensure no new WAL data is written during the copy, all writing connections must be closed or prevented from writing until the copy is finished [3]. 3. Volume Shadow Copy (VSS/Snapshots): On systems that support it, file-level snapshots (like Windows VSS or APFS snapshots) can provide a consistent point-in-time copy of the entire file system or volume, including all associated database files, provided the snapshot mechanism is truly atomic [3]. However, simply relying on standard file-copy utilities without such infrastructure is unsafe [1][2].

Citations:


Use a consistent SQLite snapshot before copying marm_memory.db.

File-level copies of marm_memory.db, -wal, and -shm can capture independent points in time if MARM writes, which may omit committed WAL frames or produce an unusable benchmark corpus. Use sqlite3.backup semantics or require the database to be quiescent before copying.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/benchmarking/README.md` around lines 54 - 55, Update the --from-live
benchmark setup to create a consistent SQLite snapshot before copying
marm_memory.db and its sidecars, using sqlite3.backup semantics or explicitly
requiring the database to be quiescent during the copy; preserve the guarantee
that the original database is never modified.

Source: Path instructions

Ten valid findings, one refuted, one already handled.

Concurrency:
- stop() now signals the in-flight build before cancelling. Cancelling only
  cancels the await around asyncio.to_thread; the extraction thread kept
  writing while unwinding released the cross-process graph lock, so another
  transport could reset the concept database underneath it.
- The lock heartbeat gave up only when a renewal was refused. A renewal that
  kept raising left the holder writing past its own expiry, which is the
  collision the lock exists to prevent. It now abandons at the TTL either way.

Schema:
- The reset no longer stamps the new version. A rebuild that died partway left
  an empty graph reporting current, so nothing prompted for the rebuild again
  and the corpus was silently absent with no queue rows to recover it. The
  version is written by the build that finishes, via mark_schema_current().
- A doc resave now retracts provenance first, matching the Console replace
  path. Indexing only adds, so repeated saves accumulated concepts from doc
  versions that no longer existed.

Tests:
- The two-process TTL test ran a synchronous subprocess on the event loop,
  blocking the heartbeat it was trying to prove. It goes through a thread now.
- Settings reload tests pin every path they resolve at tmp_path instead of
  relying on the session HOME sandbox.
- The console-delete cleanup test accepted status="skipped", which a missing
  concept database returns. It now asserts the provenance actually went.

Benchmark:
- Zero indexed memories reported "worker was busy throughout" and presented
  the deltas as valid. That is exactly how the superseded-hash bug produced a
  believable but meaningless run.
- --from-live uses sqlite3 backup() instead of copying the database and its
  WAL sidecars separately, which could capture three different instants.

Docs: package README section numbering was left broken when the indexing
benchmark section was removed, and manual mode still records queue rows, which
was undocumented.

Refuted: Codex's claim that Console replace never retracts provenance.
endpoints/memory.py:154 already does, before the queued reindex runs. Merges
are deliberate, since appended content keeps its earlier entities valid.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@marm-mcp-server/marm_mcp_server/core/concept_db.py`:
- Around line 278-279: Update the reset flow around init_concept_database() and
clear_schema_marker() so database initialization does not recreate the schema
marker after entities are dropped. Ensure the marker remains absent throughout
rebuilding, and only write it via mark_schema_current() once the complete build
succeeds.

In `@marm-mcp-server/marm_mcp_server/services/notebook.py`:
- Around line 261-272: The resave cleanup currently runs after
store_doc_mirror() queues indexing, allowing it to delete newly indexed
entities. Move the best-effort _cleanup_deleted_concepts_async call before
store_doc_mirror() commits the replacement, and pass doc_row.memory_id rather
than memory_id; preserve the existing exception handling and only apply this to
resaves.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 641adc7d-c0f8-4dda-abcf-3342e566281e

📥 Commits

Reviewing files that changed from the base of the PR and between e4ca0f8 and b875dfd.

📒 Files selected for processing (16)
  • CHANGELOG.md
  • README.md
  • marm-mcp-server/README.md
  • marm-mcp-server/marm_mcp_server/core/concept_build_lock.py
  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/core/concept_worker.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
  • marm-mcp-server/marm_mcp_server/services/notebook.py
  • marm-mcp-server/tests/test_concept_incremental_build.py
  • marm-mcp-server/tests/test_concept_two_process.py
  • marm-mcp-server/tests/test_concept_worker.py
  • marm-mcp-server/tests/test_concept_worker_wiring.py
  • marm-mcp-server/tests/test_graph_context.py
  • scripts/benchmarking/README.md
  • scripts/benchmarking/performance/bench_concept_worker.py
🚧 Files skipped from review as they are similar to previous changes (12)
  • CHANGELOG.md
  • marm-mcp-server/tests/test_concept_worker_wiring.py
  • scripts/benchmarking/README.md
  • marm-mcp-server/tests/test_concept_incremental_build.py
  • marm-mcp-server/marm_mcp_server/core/concept_build_lock.py
  • marm-mcp-server/tests/test_concept_two_process.py
  • README.md
  • marm-mcp-server/marm_mcp_server/core/concept_worker.py
  • marm-mcp-server/tests/test_concept_worker.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
  • scripts/benchmarking/performance/bench_concept_worker.py
  • marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
marm-mcp-server/marm_mcp_server/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

marm-mcp-server/marm_mcp_server/**/*.py: Keep changes surgical, touch only what the task requires, match existing style, and preserve behavior during refactors.
All memory writes must use the serialized asynchronous write queue and must not bypass its single worker.
marm_log_entry must dual-write a log_entries row and semantic memory, but semantic-store failure must never fail the log write.
Graph and concept failures must never break the seven core memory tools; graph-aware recall must keep primary memory ranking authoritative and treat graph enrichment as bounded, read-only, and fail-open.
Keep the memory and concept graph SQLite databases isolated and never share connections between their pools.
Writes must succeed when the embedding encoder is unavailable; use the single lazy-loaded 512-dimensional jinaai/jina-embeddings-v2-small-en encoder behind a lock.
Keep orchestration in its current owner file and extract modules only at real boundaries; avoid speculative abstractions and unnecessary configuration flags.
Use minimal comments only for non-obvious reasons; never add comments that narrate the next line.

Files:

  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/services/notebook.py
**/*.{py,md,json,toml,yml,yaml,Dockerfile}

📄 CodeRabbit inference engine (AGENTS.md)

Use SemVer: MAJOR for breaking changes, MINOR for new tools, parameters, or features, and PATCH for fixes and documentation updates.

Files:

  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/services/notebook.py
  • marm-mcp-server/tests/test_graph_context.py
  • marm-mcp-server/README.md
**/*.py

⚙️ CodeRabbit configuration file

**/*.py: Prioritize runtime correctness, async/concurrency safety, SQLite transaction safety, auth/rate-limit behavior, release-breaking packaging issues, and MCP protocol compatibility.

Files:

  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/services/notebook.py
  • marm-mcp-server/tests/test_graph_context.py
marm-mcp-server/tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

marm-mcp-server/tests/**/*.py: Run tests with pytest from marm-mcp-server/; use real FastAPI endpoints and real SQLite, mocking only when it meaningfully speeds testing and matches real behavior with at least 95% fidelity.
Every new MARM Console API route must have at least one happy-path FastAPI response-contract test with the MCP adapter stubbed.
Do not write existence-check or coded-to-pass tests; prefer deep tests exercising real paths, and use pytest.mark.skip only for genuinely unavailable dependencies.

Files:

  • marm-mcp-server/tests/test_graph_context.py
**/tests/**

⚙️ CodeRabbit configuration file

**/tests/**: Focus on tests that are flaky, non-isolated, incorrectly asserting behavior, or missing coverage for a changed high-risk path. Skip minor naming, comments, and layout preferences.

Files:

  • marm-mcp-server/tests/test_graph_context.py
{README.md,marm-mcp-server/README.md,marm-mcp-server/marm_mcp_server/resources/marm-docs/README.md,docs/INSTALL-*.md}

📄 CodeRabbit inference engine (AGENTS.md)

When bumping the version, update the README h1 in all three README variants and the version headers in installation documents.

Files:

  • marm-mcp-server/README.md
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Only flag documentation issues that are materially wrong, misleading for installation/release behavior, or inconsistent with live MCP behavior. Skip style, phrasing, formatting, and wording preferences.

Files:

  • marm-mcp-server/README.md
🔇 Additional comments (4)
marm-mcp-server/README.md (3)

10-10: LGTM!

Also applies to: 91-91, 145-146


161-161: LGTM!

Also applies to: 205-205, 217-217


701-704: LGTM!

Also applies to: 870-870, 879-889, 905-905, 914-914, 984-990

marm-mcp-server/tests/test_graph_context.py (1)

11-11: LGTM!

Also applies to: 186-195, 196-221, 224-253, 279-282

Comment thread marm-mcp-server/marm_mcp_server/core/concept_db.py Outdated
Comment thread marm-mcp-server/marm_mcp_server/services/notebook.py Outdated
Both valid, both narrow, both in code added by this branch.

- The reset wrote the schema version and deleted it again a moment later.
  init_concept_database takes mark_current now and the reset passes False, so
  the marker is never present on an unbuilt graph. Before this, a crash between
  the two calls, or another process reading the schema state in between, saw an
  empty graph reported as current and never asked for the rebuild again.
  clear_schema_marker is gone with its only caller.

- Doc resave cleanup ran after store_doc_mirror had already queued the
  replacement, and passed the id that call returned. Two problems: the returned
  id is a new one when the previous mirror row was deleted, so the stale
  provenance under the old id survived while the cleanup did nothing useful;
  and running after the enqueue can delete entities the worker has already
  written for the new content, leaving the queue row settled with nothing to
  re-index it. Cleanup now runs before the write, keyed on the previous id.
All three findings valid. The first was my incomplete fix, not a new issue.

- stop() set the abort flag and then cancelled, which unwound the lock and
  released it while the extraction thread was still writing. The flag shrank
  the window to one memory but did not close it. _run_build now sets a finished
  event in a finally, the only place that can know the thread stopped, and
  stop() waits on it for ABORT_GRACE_SECONDS before releasing. Bounded on
  purpose: waiting out a full extraction would put spaCy back on the teardown
  path that v2.35.0 exists to bound. Past the grace the lock is released
  anyway, and the residual risk is stray entities or a logged write failure in
  a graph another process now owns, not corruption.

- Console replace cleaned old provenance after queuing the replacement, the
  same ordering just fixed for promoted-doc resaves. Cleanup now runs first,
  guarded on the memory existing, which that path does not need: a replace
  against a missing id is an ordinary 404, and cleaning first would strip a
  live memory's provenance on the way to returning one.

- The pagination baseline still excluded compaction sources while production
  excludes summaries. Seeded rows are all NULL so it masked nothing, but the
  regression baseline disagreed with the code exactly where this feature
  changed behavior.

The stop() contract test was asserting an instant return. It now asserts the
bound instead, and that a build ignoring the abort is not waited out.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
marm-mcp-server/marm_mcp_server/endpoints/concepts.py (1)

218-236: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

finished is not signalled on every exit path of the build call chain. ConceptWorker.stop() waits up to ABORT_GRACE_SECONDS + 1 whenever self._build_finished is unset, so any path that returns or raises without setting the event adds a pointless bounded delay to shutdown. The contract stated on Line 356 of marm-mcp-server/marm_mcp_server/endpoints/concepts.py is that the event always marks the end of graph writing.

  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py#L218-L236: move _get_concept_db() and is_graph_available() inside the try so a sqlite3.Error or OSError during setup still reaches the finally that sets finished.
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py#L725-L737: set finished in a finally around the whole build_for_memory_ids body so the empty-batch return, the rebuild_required raise, and the unavailable-database raise all signal it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@marm-mcp-server/marm_mcp_server/endpoints/concepts.py` around lines 218 -
236, Ensure the build completion event is signalled on every exit path: in
concepts.py lines 218-236, move _get_concept_db() and is_graph_available()
inside the try so setup errors reach the existing completion finally; in
concepts.py lines 725-737, wrap the entire build_for_memory_ids body in a
finally that sets finished, covering empty-batch returns and all raised errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@marm-mcp-server/tests/test_concept_worker.py`:
- Around line 458-469: In the slow_build work function, update the completion
sequence so still_running.clear() executes before finished.set(). Keep the
existing delay and abort behavior unchanged, ensuring the worker no longer
reports completion until it has stopped marking itself as active.

---

Outside diff comments:
In `@marm-mcp-server/marm_mcp_server/endpoints/concepts.py`:
- Around line 218-236: Ensure the build completion event is signalled on every
exit path: in concepts.py lines 218-236, move _get_concept_db() and
is_graph_available() inside the try so setup errors reach the existing
completion finally; in concepts.py lines 725-737, wrap the entire
build_for_memory_ids body in a finally that sets finished, covering empty-batch
returns and all raised errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c7b4c7c-5fa2-4f88-8020-fb5ca50d997c

📥 Commits

Reviewing files that changed from the base of the PR and between b875dfd and 0454c98.

📒 Files selected for processing (8)
  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/core/concept_worker.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
  • marm-mcp-server/marm_mcp_server/endpoints/memory.py
  • marm-mcp-server/marm_mcp_server/services/notebook.py
  • marm-mcp-server/tests/test_concept_build_pagination.py
  • marm-mcp-server/tests/test_concept_worker.py
  • marm-mcp-server/tests/test_graph_context.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • marm-mcp-server/tests/test_graph_context.py
  • marm-mcp-server/tests/test_concept_build_pagination.py
  • marm-mcp-server/marm_mcp_server/core/concept_worker.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
marm-mcp-server/marm_mcp_server/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

marm-mcp-server/marm_mcp_server/**/*.py: Keep changes surgical, touch only what the task requires, match existing style, and preserve behavior during refactors.
All memory writes must use the serialized asynchronous write queue and must not bypass its single worker.
marm_log_entry must dual-write a log_entries row and semantic memory, but semantic-store failure must never fail the log write.
Graph and concept failures must never break the seven core memory tools; graph-aware recall must keep primary memory ranking authoritative and treat graph enrichment as bounded, read-only, and fail-open.
Keep the memory and concept graph SQLite databases isolated and never share connections between their pools.
Writes must succeed when the embedding encoder is unavailable; use the single lazy-loaded 512-dimensional jinaai/jina-embeddings-v2-small-en encoder behind a lock.
Keep orchestration in its current owner file and extract modules only at real boundaries; avoid speculative abstractions and unnecessary configuration flags.
Use minimal comments only for non-obvious reasons; never add comments that narrate the next line.

Files:

  • marm-mcp-server/marm_mcp_server/services/notebook.py
  • marm-mcp-server/marm_mcp_server/endpoints/memory.py
  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
**/*.{py,md,json,toml,yml,yaml,Dockerfile}

📄 CodeRabbit inference engine (AGENTS.md)

Use SemVer: MAJOR for breaking changes, MINOR for new tools, parameters, or features, and PATCH for fixes and documentation updates.

Files:

  • marm-mcp-server/marm_mcp_server/services/notebook.py
  • marm-mcp-server/marm_mcp_server/endpoints/memory.py
  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
  • marm-mcp-server/tests/test_concept_worker.py
**/*.py

⚙️ CodeRabbit configuration file

**/*.py: Prioritize runtime correctness, async/concurrency safety, SQLite transaction safety, auth/rate-limit behavior, release-breaking packaging issues, and MCP protocol compatibility.

Files:

  • marm-mcp-server/marm_mcp_server/services/notebook.py
  • marm-mcp-server/marm_mcp_server/endpoints/memory.py
  • marm-mcp-server/marm_mcp_server/core/concept_db.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
  • marm-mcp-server/tests/test_concept_worker.py
marm-mcp-server/marm_mcp_server/endpoints/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Keep endpoint logic in endpoints/, split by surface, and keep shared helpers in core/. New tools should be implemented in the appropriate endpoint module.

Files:

  • marm-mcp-server/marm_mcp_server/endpoints/memory.py
  • marm-mcp-server/marm_mcp_server/endpoints/concepts.py
marm-mcp-server/tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

marm-mcp-server/tests/**/*.py: Run tests with pytest from marm-mcp-server/; use real FastAPI endpoints and real SQLite, mocking only when it meaningfully speeds testing and matches real behavior with at least 95% fidelity.
Every new MARM Console API route must have at least one happy-path FastAPI response-contract test with the MCP adapter stubbed.
Do not write existence-check or coded-to-pass tests; prefer deep tests exercising real paths, and use pytest.mark.skip only for genuinely unavailable dependencies.

Files:

  • marm-mcp-server/tests/test_concept_worker.py
**/tests/**

⚙️ CodeRabbit configuration file

**/tests/**: Focus on tests that are flaky, non-isolated, incorrectly asserting behavior, or missing coverage for a changed high-risk path. Skip minor naming, comments, and layout preferences.

Files:

  • marm-mcp-server/tests/test_concept_worker.py
🔇 Additional comments (9)
marm-mcp-server/marm_mcp_server/services/notebook.py (1)

241-259: LGTM!

marm-mcp-server/marm_mcp_server/endpoints/memory.py (1)

139-165: LGTM!

marm-mcp-server/marm_mcp_server/endpoints/concepts.py (1)

688-702: LGTM!

marm-mcp-server/marm_mcp_server/core/concept_db.py (3)

43-50: LGTM!


170-179: LGTM!


261-285: LGTM!

marm-mcp-server/tests/test_concept_worker.py (3)

152-159: LGTM!

Also applies to: 179-188, 286-291


375-408: LGTM!


411-441: 🩺 Stability & Availability

No change needed.

stop() has a fixed 3-second bounded timeout, so this test does not “pay” the full ABORT_GRACE_SECONDS as an unbounded teardown step.

			> Likely an incorrect or invalid review comment.

Comment on lines +458 to +469
async def slow_build(memory_ids, abort=None, finished=None):
# Stands in for a thread that notices the abort and then takes a
# moment to unwind, which is when the lock must still be held.
def work():
abort.wait(5)
time.sleep(0.4)
if finished is not None:
finished.set()
still_running.clear()

await asyncio.to_thread(work)
return {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Reorder finished.set() and still_running.clear() to remove a flake window.

stop() unblocks as soon as finished is set. still_running.clear() runs after that on the worker thread. Between the two statements, stop() can cancel the task, unwind the lock context, and call watching_release while still_running is still set. The test then records a false violation.

The window is small, so the test passes on an idle machine. On a loaded CI runner the worker thread can be descheduled between the two statements.

Clear still_running first. The event models "the thread is still writing", so it must be cleared before the thread announces it has finished.

🔧 Proposed fix
         def work():
             abort.wait(5)
             time.sleep(0.4)
+            still_running.clear()
             if finished is not None:
                 finished.set()
-            still_running.clear()

As per path instructions: "Focus on tests that are flaky, non-isolated, incorrectly asserting behavior, or missing coverage for a changed high-risk path."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def slow_build(memory_ids, abort=None, finished=None):
# Stands in for a thread that notices the abort and then takes a
# moment to unwind, which is when the lock must still be held.
def work():
abort.wait(5)
time.sleep(0.4)
if finished is not None:
finished.set()
still_running.clear()
await asyncio.to_thread(work)
return {}
async def slow_build(memory_ids, abort=None, finished=None):
# Stands in for a thread that notices the abort and then takes a
# moment to unwind, which is when the lock must still be held.
def work():
abort.wait(5)
time.sleep(0.4)
still_running.clear()
if finished is not None:
finished.set()
await asyncio.to_thread(work)
return {}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@marm-mcp-server/tests/test_concept_worker.py` around lines 458 - 469, In the
slow_build work function, update the completion sequence so
still_running.clear() executes before finished.set(). Keep the existing delay
and abort behavior unchanged, ensuring the worker no longer reports completion
until it has stopped marking itself as active.

Source: Path instructions

@Lyellr88
Lyellr88 merged commit 833a9fa into MARM-main Aug 2, 2026
7 checks passed
@Lyellr88
Lyellr88 deleted the release/v2.36.0 branch August 2, 2026 10:39
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.

1 participant