Repository navigation
Description
Environment
- Source tag:
DeusData/codebase-memory-mcpv0.8.1 - Resolved source commit:
f0c9be19c5d74b84f418d807bfdce7b5d6a261ff(lightweight tag; verified bygit ls-remoteand the GitHub APIgit/refs/tags/v0.8.1endpoint) - Binary variant: standard non-UI
- Build: locally compiled on macOS 10.15.8 Catalina x86_64 with Apple Clang 12.0.0 (
clang-1200.0.32.29);MACOSX_DEPLOYMENT_TARGET=10.15;CC=cc;CXX=c++; the resultingLC_BUILD_VERSIONisminos=10.15, sdk=10.15.6(Catalina-compatible). - Binary SHA-256:
5a859337d243a0f4be913d764bb73a0e01f83e68225cf63c0f000e468df166de - Cache: isolated
CBM_CACHE_DIRper test (no shared or prior-pilot caches were used) - Fixture: a generic 3-file React project at
/Users/test/cbm-repro/sample-react-project/(package.json,src/main.tsx,src/App.tsx) so that no Astryx or other private project names appear in the cache filenames or in the indexer's output - No installer script, no
install.sh, no auto-configuration of any agent. The binary is driven exclusively over raw stdio MCP (bin-source-built/codebase-memory-mcpinvoked as a subprocess;initialize,notifications/initialized,tools/list,tools/call; nothing else)
Expected behavior
After the MCP process exits (any reason) and is restarted with the same CBM_CACHE_DIR:
- The previously-indexed project remains registered.
index_status,get_graph_schema,get_architecture,search_graph,search_code, andget_code_snippetall return the previously-indexed data.- Either the WAL is checkpointed on graceful close, or the WAL is recovered on next start, so that the project record is visible in the main
.dbfile after restart. - The project's
.db-waland.db-shmsidecar files (if any) are either checkpointed (truncated to zero) on close, or they reflect a state that the next start can recover from.
Actual behavior
Before termination (in the same process):
initialize,notifications/initialized,tools/listsucceed.index_repositorysucceeds withstatus:"indexed",nodes:8,edges:10for the 3-filesample-react-projectfixture.list_projectsreturns the project withname,root_path,nodes:8,edges:10,size_bytes:1769472.index_statusreturns{"project":"…","nodes":8,"edges":10,"status":"ready"}.search_codereturns the project file matches.
After termination and restart (separate fresh process, same CBM_CACHE_DIR):
-
list_projectsreturns the same project entry (samename,nodes,edges,size_bytes). -
index_statusreturns:{ "error": "project not found or not indexed", "hint": "Use list_projects to see all indexed projects, then pass the project name.", "available_projects": ["…sample-react-project"], "count": 1 } -
search_codereturns the same"project not found or not indexed"error. -
The project's
.dbfile remains on disk with the same size (1,769,472 bytes) and the same SHA-256 as immediately before termination. -
After the restart sequence runs queries against the same cache,
.db-waland.db-shmsidecar files appear in the cache directory and persist.
The project's file is on disk; the project is in list_projects; but the query tools return "project not found". The project is registered but unreachable through queries.
Process shutdown behavior (kept separate from the persistence defect)
The lifecycle tests used four independent caches, each starting empty, each receiving one termination event. The exit-behavior was observed as follows:
| Test | Termination sent | Process exited within 15 s window? | Exit code | Time to exit |
|---|---|---|---|---|
| A | close client stdin cleanly (no signal) | yes | 0 | 0.0 s |
| B | SIGINT | yes | 0 | 0.41 s |
| C | SIGTERM | yes | 0 | 0.21 s |
| D | SIGKILL | yes | -9 | 0.2 s |
In the four isolated tests above, the process always exited within the test window. However, a separate controlled run (in a different prior pilot) observed the binary remaining in S (sleeping) state when SIGTERM was sent; that observation is out of scope for this issue and is reported separately if relevant. The persistence defect observed here is independent of which termination mode was used: in all four tests, the .db file remained on disk with the same size, and after the restart all query tools reported "project not found" while list_projects continued to see the project.
Diagnostic result (disposable cache only)
In an isolated disposable cache, after the restart sequence returned the "project not found" responses, removing the .db-wal and .db-shm sidecar files caused the project's queries to succeed again on the next restart (without re-indexing). The .db file itself was not deleted and was not modified.
This is a diagnostic action performed on a disposable test cache only to isolate the failure to the sidecar files. It is not proposed as a production workaround. The implication is that the sidecar files, when present, cause the query open path (cbm_store_open_path_query in mcp.c's resolve_store) to either fail the integrity check or fail the project-record lookup against the main .db file. A production fix should make the open path tolerate (or actively recover from) the sidecar state, not require users to delete sidecars.
Compact before/after cache inventory
This is the cache inventory observed in one of the four tests (the others are identical except for the project-DB SHA-256, which is unique per test). All numbers are exact and reproducible; see evidence/upstream-prep/lifecycle-*/test.log for the full per-test logs.
| Stage | File | Size (bytes) | SHA-256 (first 16 hex) |
|---|---|---|---|
| Before termination | …sample-react-project.db |
1,769,472 | b7e13d8b157b09a7 (test B; varies per test) |
| Before termination | _config.db |
12,288 | e8f7566de75fe3f6 (constant across all tests) |
| Immediately after exit | …sample-react-project.db |
1,769,472 | b7e13d8b157b09a7 (unchanged) |
| Immediately after exit | _config.db |
12,288 | e8f7566de75fe3f6 (unchanged) |
| After restart sequence (queries failed) | …sample-react-project.db |
1,769,472 | b7e13d8b157b09a7 (unchanged) |
| After restart sequence (queries failed) | _config.db |
12,288 | e8f7566de75fe3f6 (unchanged) |
The project DB is preserved across the lifecycle event in all four tests. Across the four tests, the project DB SHA-256s differ (each indexing run produces a fresh DB) but the sizes are identical (1,769,472 bytes), and within each test the project DB SHA-256 is identical before, after exit, and after restart.
Relationship to PR #387 (merged) — context only
PR #387 ("fix(store): checkpoint WAL on close and startup to prevent orphan accumulation") was intended to:
- Checkpoint the WAL before close, so the next open sees the same rows the previous process wrote.
- Recover stale WAL at startup (
PRAGMA wal_checkpoint(PASSIVE)), so an ungraceful exit doesn't poison the next open. - Prevent orphan WAL/SHM sidecar accumulation.
The v0.8.1 reproduction above indicates that, under the tested stdio lifecycle, at least one of these three is incomplete or has regressed: the sidecar files are present in the cache after the restart, the .db is on disk, but the query open path does not return the project's data. A subsequent restart in a fresh cache (where the indexer wrote the data and exited cleanly) is what the diagnostic above measures; the sidecar state from a prior failed restart can be cleared by deleting the sidecars, but the user's expectation is that the binary's own close/restart logic handles this.
PR #387 should be checked for whether the close-time checkpoint covers the project-record write path and whether the startup-time recovery handles the case where the .db is on disk but the sidecars are present.
Relationship to issue #277 (open) — context only
Issue #277 ("New files not indexed — WAL-checkpoint blocked on successfully-indexed project") is in the same WAL/recovery family. The defect observed here is not the same symptom (here: queryable in the same process, unqueryable after restart; #277: new files not detected). However, the underlying root-cause family (WAL state not being committed before close, or not being recovered on next open) is plausibly the same.
Relationship to issue #557 (open) — context only
Issue #557 describes a different defect: the binary unlinks the project DB on a corruption determination (deleting corrupt db path in resolve_store). The v0.8.1 reproduction in this issue does not reproduce that symptom. In all four tests above, the .db file is preserved with the same size and the same SHA-256. The defect in this issue is a lesser manifestation of the same WAL/recovery area: the sidecar files are present and the next open cannot read the project, but the file is not unlinked. (See the issue-557 family discussion above for the "delete corrupt db" code path; the controlled lifecycle isolation in this issue's tests did not reach that path.)
Minimal reproduction
All commands below are public-safe; no private names, paths, or model names appear. The fixture is generic; the cache is disposable; the reproduction is one shell command plus one Python script.
# Variables (set BINARY, CACHE, FIXTURE; create an empty CACHE; use a small generic fixture)
BINARY=/Users/test/cbm-repro/bin-source-built/codebase-memory-mcp # locally compiled v0.8.1
CACHE=/Users/test/cbm-repro/cache-wal-restart-XXXX # fresh, empty
FIXTURE=/Users/test/cbm-repro/sample-react-project # 3 files
mkdir -p "$CACHE"
# Step 1 — start the binary, index, and confirm queryability in the same process.
# Step 2 — terminate (any method; here we use stdin close which is the most reproducible).
# Step 3 — restart with the same CACHE.
# Step 4 — list_projects still works; index_status and search_code return "project not found".
# Step 5 — diagnostic: remove sidecars, restart, queries succeed again.
#
# All four lifecycle tests (close-stdin / SIGINT / SIGTERM / SIGKILL) produce the same Step 4 result.
python3 - <<'PY'
import os, json, select, subprocess, time, signal
BINARY, CACHE, FIXTURE = os.environ['BINARY'], os.environ['CACHE'], os.environ['FIXTURE']
def send(p, r):
p.stdin.write((json.dumps(r) + "\n").encode())
p.stdin.flush()
def recv(p, rid, t=15):
end = time.time() + t
buf = b""
while time.time() < end:
r, _, _ = select.select([p.stdout], [], [], 0.3)
if r:
c = os.read(p.stdout.fileno(), 4096)
if not c: return None
buf += c
while b"\n" in buf:
l, buf = buf.split(b"\n", 1)
if not l.strip(): continue
try: x = json.loads(l.decode())
except: continue
if x.get("id") == rid: return x
return None
def text_of(resp):
if not resp or "result" not in resp: return ""
return resp["result"].get("content", [{}])[0].get("text", "")
# --- Pass 1: index + query, then exit via stdin close ---
p = subprocess.Popen([BINARY], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0,
env={**os.environ, "CBM_CACHE_DIR": CACHE})
send(p, {"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2024-11-05","capabilities":{},
"clientInfo":{"name":"r","version":"1"}}})
recv(p, 1)
send(p, {"jsonrpc":"2.0","method":"notifications/initialized","params":{}})
send(p, {"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"index_repository","arguments":{"repo_path": FIXTURE}}})
recv(p, 2, 60)
send(p, {"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"index_status","arguments":{"project":"sample-react-project"}}})
print("index_status in pass 1:", text_of(recv(p, 3)))
p.stdin.close() # close stdin; this is the "EOF" termination mode
# --- Pass 2: restart with the same CACHE ---
p = subprocess.Popen([BINARY], stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0,
env={**os.environ, "CBM_CACHE_DIR": CACHE})
send(p, {"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2024-11-05","capabilities":{},
"clientInfo":{"name":"r","version":"1"}}})
recv(p, 1)
send(p, {"jsonrpc":"2.0","method":"notifications/initialized","params":{}})
send(p, {"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"list_projects","arguments":{}}})
print("list_projects in pass 2:", text_of(recv(p, 2)))
send(p, {"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"index_status","arguments":{"project":"sample-react-project"}}})
print("index_status in pass 2:", text_of(recv(p, 3)))
send(p, {"jsonrpc":"2.0","id":4,"method":"tools/call",
"params":{"name":"search_code","arguments":{"pattern":"App","project":"sample-react-project"}}})
print("search_code in pass 2:", text_of(recv(p, 4)))
p.stdin.close()
PY
# Inspect the cache; the project .db is on disk; the .db-wal and .db-shm may also be present.
ls -la "$CACHE"
# Diagnostic only — disposable cache. Do NOT do this in production.
rm -f "$CACHE"/*.db-wal "$CACHE"/*.db-shm
# Restart again; index_status and search_code now succeed without re-indexing.Why this is not just a CLI-wrapper or field-name issue
The four raw-MCP query tools tested in this issue (index_status, search_code, list_projects, get_architecture) all use the live schema's canonical project field (the field name is project, not project_name; the related CLI vs MCP schema mismatch is reported in a separate draft). The query calls in this issue's reproduction succeed in the same process and fail in the restart process with the exact same field values. The defect is in the server-side state at the moment of cbm_store_open_path_query in resolve_store, not in the client's field name.
Local verdict
The local controlled pilot verdict is CODEBASE MEMORY PILOT BLOCKED — NO OPENCLAW CONFIG CHANGES RETAINED. The locally-compiled v0.8.1 binary has not been registered as an MCP server in the test environment; openclaw.json was not modified; no skills, hooks, or instructions were installed. No part of this controlled pilot was performed against an authoritative or production repository. The standard non-UI binary and a 3-file generic React fixture were used. All cache directories used by the test were disposable and isolated per test.
Activity
DeusData commentedon Jul 3, 2026
Thanks for the careful restart/WAL sidecar report. Triage: this belongs under index/store recovery and large-repo stability (#391/#593).
The project list must not advertise a project as usable if the store cannot be opened after restart, and stale WAL/SHM sidecars need a deterministic recovery or cleanup path. The regression should cover restart/open behavior, not just initial indexing.
DeusData commentedon Jul 11, 2026
Strong report — and the evidence points somewhere interesting: after restart, list_projects still reads the DB fine (it opens each cache file and counts your 8 nodes / 10 edges), while index_status fails to resolve the project by NAME. That's the signature of the store-resolution bug (#704/#640: project-name-to-DB-file resolution drifting from the DB's internal name), which was fixed in v0.9.0 — your v0.8.1 build predates it. Separately, this week's 0.9.1 work also hardened the exact WAL/sidecar class your title suspects (#897: stale sidecars replayed over fresh DBs; #896: corruption now errors loudly instead of returning truncated results; #895: torn artifact exports).
Could you rerun your (excellent) isolated-cache repro on v0.9.0? If index_status resolves after restart, we'll close this as fixed by the resolution work; if it still fails, your setup is precise enough that we'd want the exact cache filename + the internal name (
sqlite3 <db> 'SELECT name FROM projects;') to compare.github-actions commentedon Jul 11, 2026
Thanks for the report! To move this forward we need a bit more so we can reproduce it ourselves:
codebase-memory-mcp --versionyou're onOnce that's here we'll pick it straight back up. Heads-up: issues left
awaiting-reporterare automatically closed after a few weeks of silence, but a comment reopens the door anytime.github-actions commentedon Aug 1, 2026
This issue has been waiting on more information for 21 days (a version, exact steps, or a public repro), so it's now marked
stale. It will not be closed automatically — a maintainer reviews stale issues by hand. Adding a comment with the details clears the label and puts it straight back in the queue. We're happy to pick it back up the moment we can reproduce it.DeusData commentedon Sep 25, 2026
Thank you, @humais, for a model bug report: isolated caches, all four termination modes, and a runnable script! We ran your lifecycle against current main: index, then stdin EOF or SIGKILL, then restart, then
index_statusandsearch_code. The project resolves in both modes, including with.db-waland.db-shmfiles present. The fixes came in two steps: project-name resolution in v0.9.0, then stale-sidecar handling plus name-tail resolution in v0.10.0. Could you re-run your script on v0.11.0? If it still reproduces, the cache filename and the output ofsqlite3 <db> 'SELECT name FROM projects;'would let us compare directly.github-actions commentedon Oct 18, 2026
This issue has been waiting on more information for 21 days (a version, exact steps, or a public repro), so it's now marked
stale. It will not be closed automatically — a maintainer reviews stale issues by hand. Adding a comment with the details clears the label and puts it straight back in the queue. We're happy to pick it back up the moment we can reproduce it.DeusData commentedon Oct 19, 2026
Thank you again for the careful isolated-cache reproduction. Checking back on the September request: have you been able to rerun it on a newer version, and does the project remain queryable after restart? No new fix claim is being made here, and deleting sidecars from a working cache is not being requested.
DeusData commentedon Oct 25, 2026
Thank you again for the report. If you have safely retested the isolated-cache case, please share whether the project remains queryable after restart. Please do not delete sidecars from a working cache.