Guide for upgrading between released versions. Current: v0.10.4.
ClawMem upgrades are designed to be drop-in: pull the new version, restart any long-lived processes, and the SQLite schema auto-migrates on first open. This guide documents per-version specifics for upgrades that have additional considerations beyond the quick path below.
# Option A: npm / bun global install
bun update -g clawmem # or: npm update -g clawmem
# Option B: source install
cd ~/clawmem && git pull
# Restart long-lived processes to pick up the new code
systemctl --user restart clawmem-watcher.service # if installed as a user unit
Hooks (spawned fresh per Claude Code invocation) and the MCP stdio server (respawned per Claude Code session) pick up new code automatically on their next invocation — no restart required for those. Only persistent daemons like clawmem watch, clawmem serve, and the systemd embed/watcher/curator units need to be restarted.
All schema changes from v0.7.1 → v0.9.0 are additive and idempotent:
CREATE TABLE IF NOT EXISTSALTER TABLE ADD COLUMN wrapped in try/catchCREATE INDEX IF NOT EXISTS (v0.9.0 adds idx_entity_nodes_lower_name on entity_nodes(LOWER(name), vault) — built idempotently on first open)The first time any v0.7.1+ process opens an existing vault, the migrations run silently. Hook invocations alone are sufficient — you do not need a manual upgrade command.
clawmem embed — embedding contract and fragment boundaries are unchanged across v0.7.x → v0.9.0clawmem reindex — document storage is unchangedclawmem reindex --enrich — no new enrichment stages added in v0.9.0clawmem build-graphs — no new graph edge typesclawmem setup hooks — hook configuration is unchanged (no new hooks, no renamed hooks, no changed budgets)bun install / npm install — no dependency changes in package.json between v0.7.0 and v0.9.0~/.config/clawmem/config.yaml — no required fields addedv0.10.4 fixes issue #11 — clawmem setup openclaw previously hardcoded ~/.openclaw/extensions/clawmem and ignored OPENCLAW_STATE_DIR, breaking installs into custom OpenClaw profiles. The fix is non-breaking: vault on disk is byte-identical, no schema changes, no env-var changes for users on the default profile, no retrieval-pipeline or hook changes. Pure bun update -g clawmem.
clawmem setup openclaw is now profile-aware. When the openclaw CLI is on PATH, ClawMem delegates to openclaw plugins install <pluginDir> --force. OpenClaw owns destination resolution, which respects OPENCLAW_STATE_DIR, OPENCLAW_CONFIG_PATH, and any active --profile flag. The plugin is auto-enabled as part of the install (OpenClaw’s persistPluginInstall writes install records, applies slot selection, and refreshes the registry). The post-install “Next steps” output no longer prints openclaw plugins enable clawmem because the install path already did it.openclaw is not on PATH, ClawMem falls back to a recursive copy that also honors OPENCLAW_STATE_DIR (via a faithful mirror of OpenClaw’s resolveConfigDir). The fallback path keeps the original 4-step output including openclaw plugins enable clawmem because direct-copy doesn’t auto-enable.--link mode now does the right thing in both paths. The delegated path invokes openclaw plugins install -l, which records the source in plugins.load.paths (NOT a filesystem symlink) — discovery uses the load-path entry, so the v2026.4.11 symlink-discovery skip does NOT apply. The fallback path still creates a real filesystem symlink, which OpenClaw v2026.4.11+ skips during discovery; install OpenClaw to get the cleaner delegated behavior.--remove is legacy-compatible. clawmem setup openclaw --remove first tries openclaw plugins uninstall clawmem --force (when the CLI is available) and falls back to manual cleanup at the resolved extensions path for legacy unmanaged installs from earlier ClawMem versions. On CLI uninstall failure ClawMem warns the user that OpenClaw config and install records may still need manual repair, then runs the fallback cleanup.clawmem setup openclaw --help works. Pre-v0.10.4 ran the install instead of printing help. v0.10.4 short-circuits the --help / -h flag at the top of the handler and prints the full flag + env-var reference.If you run OpenClaw with a non-default profile, the upgrade is now a one-liner:
# Pre-v0.10.4 (broken): installed into ~/.openclaw regardless of OPENCLAW_STATE_DIR
OPENCLAW_STATE_DIR=~/.openclaw-dev clawmem setup openclaw
# v0.10.4+: installs into ~/.openclaw-dev/extensions/clawmem as expected
OPENCLAW_STATE_DIR=~/.openclaw-dev clawmem setup openclaw
If you previously worked around the bug by manually copying the plugin directory into your profile, you can now run clawmem setup openclaw --remove (in the affected profile via OPENCLAW_STATE_DIR) and then a fresh clawmem setup openclaw to land cleanly. Or leave the manual copy in place — v0.10.4 is backwards-compatible and --remove will fall back to manual cleanup if the CLI uninstall fails because the install isn’t in OpenClaw’s records.
# Source install
cd ~/clawmem && git pull
# Or via npm/bun
bun update -g clawmem
# Re-run setup so any new behavior takes effect (idempotent)
clawmem setup openclaw
# Restart long-lived processes (only if you re-installed the plugin)
sudo systemctl restart openclaw-gateway.service # or your gateway unit
No schema migration. No vault changes. The OpenClaw plugin source under src/openclaw/ is unchanged from v0.10.3 — the change is entirely in cmdSetupOpenClaw (and a new src/openclaw-paths.ts helper module that mirrors OpenClaw’s path-resolution semantics for the CLI-absent fallback). See docs/guides/openclaw-plugin.md for the full Install reference and docs/troubleshooting.md for the symptom→fix entries this release closes.
v0.10.0 is the OpenClaw pure-memory migration (§14.3). It changes how the ClawMem plugin registers with OpenClaw and updates clawmem setup openclaw to produce a layout that the v2026.4.11+ plugin discoverer actually finds. There are no schema changes, no new env vars, and no changes to the retrieval pipeline, hook set, or agent tools. The vault on disk is byte-identical to v0.9.0. Claude Code users who do not run OpenClaw can upgrade with no action beyond git pull.
Why the migration, in one paragraph. OpenClaw and Hermes have converged on a two-surface plugin model — one slot for memory plugins (cross-session, retrieval-first) and a separate slot for context-engine plugins (in-session, compaction-first). Under that model ClawMem is a memory layer, not a context engine, and Hermes has always had it plugged in correctly via MemoryProvider. Pre-v0.10.0 the OpenClaw integration occupied the context-engine slot only because OpenClaw had no separate memory slot at the time. v0.10.0 moves ClawMem to the OpenClaw memory slot and frees the context-engine slot for genuine compression/compaction plugins like lossless-claw. You can now run both at once. See RELEASE_NOTES.md and docs/guides/openclaw-plugin.md for the full rationale.
OpenClaw users MUST upgrade to OpenClaw v2026.4.11+ before running v0.10.0’s clawmem setup openclaw. The new discovery contract and the new install layout both depend on behavior that only exists in OpenClaw v2026.4.11 and later.
# Source install
cd ~/clawmem && git pull
# Restart long-lived daemons (still needed if you run the watcher / serve as a user unit)
systemctl --user restart clawmem-watcher.service
# OpenClaw users: re-run setup so the plugin dir switches from symlink to recursive copy
clawmem setup openclaw
# Multi-user installs only: chown the new plugin dir to the gateway user
# (see docs/guides/openclaw-plugin.md for the full ownership gotcha)
sudo chown -R <openclaw-gateway-user>:<gateway-group> ~/.openclaw/extensions/clawmem
# Then restart the gateway so it re-discovers the plugin
sudo systemctl restart openclaw-gateway.service # or whatever your gateway unit is called
Hooks and the stdio MCP server pick up the new binary automatically on their next invocation — no clawmem setup hooks re-run needed.
kind: memory, not kind: context-engine. The adapter in src/openclaw/ no longer exposes a ClawMemContextEngine class. Lifecycle events on the plugin-hook bus: before_prompt_build is the load-bearing path, running prompt-aware retrieval AND the pre-emptive precompact-extract synchronously when token usage approaches the compaction threshold (captures state strictly before the LLM call that could trigger compaction, no race with the compactor); agent_end runs decision-extractor + handoff-generator + feedback-loop in parallel; before_compaction is defense-in-depth fallback only — fire-and-forget at OpenClaw’s call site, races the compactor, exists for the rare case where the proximity heuristic in before_prompt_build missed a sudden token jump; session_start registers the session and caches first-turn bootstrap. The retrieval pipeline, composite scoring, profiles, vault format, and the 5 registered agent tools are unchanged. This is a packaging and registration change, not a behavioral one. v0.3.0 did the pre-emptive extraction from ContextEngine.compact() via delegateCompactionToRuntime(); v0.10.0 moves it up the stack into before_prompt_build where it has a real pre-LLM hook to await on, and demotes the compaction-entry-point handler to the fallback role.plugins.slots.memory: "clawmem" replaces plugins.slots.contextEngine: "clawmem". On the new pure-memory plugin, the exclusive slot is memory. The setup openclaw next-steps output tells you to run openclaw plugins enable clawmem, which sets the slot and disables competing memory plugins (memory-core, memory-lancedb) in a single command. You do NOT need to run the older openclaw config set plugins.slots.contextEngine clawmem pattern on v0.10.0.src/openclaw/package.json is now the plugin’s discovery manifest. OpenClaw v2026.4.11’s discoverInDirectory reads package.json for the openclaw.extensions field and uses that to decide whether a directory under ~/.openclaw/extensions/ is a valid plugin. The older openclaw.plugin.json manifest is still shipped and parsed at runtime, but it is not sufficient to pass discovery on v2026.4.11+ without the package.json companion file. v0.10.0 adds the package.json to the plugin source tree, and clawmem setup openclaw verifies it is present before copying.clawmem setup openclaw defaults to recursive copy instead of symlink. OpenClaw v2026.4.11 walks ~/.openclaw/extensions/ with readdirSync({ withFileTypes: true }) and uses dirent.isDirectory() to descend into candidate plugin directories. Symlinks to directories report isDirectory() === false on that API shape, so a symlinked plugin is silently skipped during discovery. v0.10.0’s cmdSetupOpenClaw therefore copies the plugin source into ~/.openclaw/extensions/clawmem/ with cpSync(..., { recursive: true, dereference: true }). A --link opt-in flag preserves the old symlink behavior for local development and for older OpenClaw versions, with a warning that v2026.4.11+ discovery will skip the symlink. Setup is idempotent: any existing plugin directory or stale symlink is removed before the new copy is written.openclaw) and you run clawmem setup openclaw as a different user (e.g. sciros), the copied plugin directory is owned by the installer user, but OpenClaw’s ownership check rejects it with suspicious ownership (uid=1001, expected uid=997 or root). This is a security feature that prevents a privileged gateway process from loading code a less-privileged user dropped into its extensions directory. Fix: sudo chown -R <gateway-user>:<gateway-group> ~/.openclaw/extensions/clawmem. Single-user installs where you ARE the gateway user are not affected — your own user owns the copy, and the ownership check passes.Rolling back to v0.9.0 is a git checkout v0.9.0 && clawmem setup openclaw --link away. The --link flag produces the old symlink layout, which is what v0.9.0 expected. If you are rolling back because you are on an OpenClaw version older than v2026.4.11, the symlink layout will still work (pre-v2026.4.11 discovery did not require the package.json file and did not have the dirent.isDirectory() gate). You do not need to downgrade OpenClaw.
If you rolled back and still see context engine 'clawmem' is not registered, remove the stale slot config: openclaw config set plugins.slots.memory "" and openclaw config set plugins.slots.contextEngine clawmem, then restart the gateway.
clawmem embed — embedding contract is unchangedclawmem reindex — document storage is unchangedclawmem reindex --enrich — no new enrichment stages in v0.10.0clawmem build-graphs — no new graph edge typesclawmem setup hooks — Claude Code hook configuration is unchangedbun install / npm install — no dependency changesv0.9.0 adds two new context-surfacing features — <vault-facts> KG injection and session-scoped focus topic boost — and is drop-in safe. One idempotent expression-index migration, no breaking API changes, no schema rewrites, no reindex/embed/graph-build needed. All behavior changes on existing code paths are additive and fail-open: if the new stages don’t fire (no entity seeds from the prompt, no focus file set), the <vault-context> output is byte-identical to v0.8.5.
bun update -g clawmem # or: npm update -g clawmem
# Or source install:
cd ~/clawmem && git pull
# Restart long-lived daemons so they pick up the new context-surfacing stages
systemctl --user restart clawmem-watcher.service
# If you run `clawmem serve` or `clawmem watch` in systemd, restart those too.
Hooks + MCP stdio pick up new code automatically on next invocation — no restart needed for those.
store.ts runs CREATE INDEX IF NOT EXISTS idx_entity_nodes_lower_name ON entity_nodes(LOWER(name), vault) on first open. Idempotent. Backs the §11.1 batch LOWER(name) IN (...) AND vault = ? lookup on the entity-detection hot path. Without this index the batch query would degrade to a full scan on large vaults. No action needed from you — the migration runs once on the first process that opens the vault.PROFILES gains a new factsTokens field. Defaults: speed=0 (stage off), balanced=200, deep=250. If you have a custom profile wrapper or override PROFILES in code, add the field. Default behavior unchanged.<vault-facts> block in <vault-context>When the user’s prompt mentions entities already known to the vault (via entity_nodes), context-surfacing now appends a token-bounded <vault-facts> block of raw SPO triple lines to <vault-context>, alongside the existing <facts> / <relationships> blocks. This feeds the model current-state knowledge about entities the user is talking about, without requiring the agent to call kg_query explicitly.
default:project:clawmem) → proper-noun extraction via resolveEntityTypeExact → longer-first n-gram scan (3-gram > 2-gram > 1-gram) for lowercase/hyphenated vocabulary like forge-stack, oauth2, vm 202. All three paths run prompt-only — entity seeds NEVER come from surfaced doc bodies, so topic-boosted off-topic docs cannot pollute the facts block.factsTokens=0 on speed disables the stage entirely. balanced uses 200 tokens, deep uses 250. The sub-budget is dedicated — <vault-facts> cannot steal budget from <facts> or <relationships>.vault-context unchanged.No configuration required. You can verify the block appears by running echo "tell me about <some entity from entity_nodes>" | clawmem surface --context --stdin after upgrade.
clawmem focus CLI — session-scoped topic boostThree new subcommands write a per-session focus file that steers context-surfacing for that session only:
clawmem focus set "authentication flow" # uses CLAUDE_SESSION_ID env var
clawmem focus set "authentication flow" --session-id abc123 # explicit
clawmem focus show --session-id abc123
clawmem focus clear --session-id abc123
When a focus topic is set:
intent hint to expandQuery / rerank / extractSnippet (the existing query-time lever).applyCompositeScoring and BEFORE the adaptive threshold filter.compositeScore, so the baseline threshold filter sees byte-identical ordering and the result set never shrinks because of a non-matching topic. Locked in by hook-level integration tests.Session isolation contract: the focus file is keyed by sessionId and never writes to SQLite, never mutates confidence / status / snoozed_until / any lifecycle column. Concurrent sessions on the same host cannot cross-contaminate each other’s topic biasing. CLAWMEM_SESSION_FOCUS env var is a debug-only override that does NOT provide per-session scoping — do not rely on it in multi-session deployments. CLAWMEM_FOCUS_ROOT override is available for hermetic testing.
clawmem embed — no embedding changesclawmem reindex — no document storage changesclawmem reindex --enrich — no new enrichment stagesclawmem build-graphs — §11.1 reads from existing entity_triples populated by the v0.8.5 SPO pipelineclawmem setup hooks — hook configuration is unchanged; Claude Code invokes ${binPath} hook ${name} at runtime, so upgrading the binary propagates the new context-surfacing behavior automaticallyIf you need to roll back to v0.8.5, the idx_entity_nodes_lower_name index is harmless on pre-v0.9.0 code — SQLite will simply ignore it. No data cleanup required, no compatibility shim needed.
v0.8.5 is a drop-in fix for the SPO triple extraction bug cluster (see RELEASE_NOTES.md entry for the full bug list). entity_triples stayed at zero on pre-v0.8.5 vaults regardless of activity, making kg_query return empty for every entity. v0.8.5 fixes the population path end-to-end. No schema changes, no new env vars, no new dependencies, no breaking API changes. All behavior changes are additive.
bun update -g clawmem # or: npm update -g clawmem
# Or source install:
cd ~/clawmem && git pull
# Restart long-lived daemons so they pick up the new decision-extractor pipeline
systemctl --user restart clawmem-watcher.service
Claude Code hooks and the stdio MCP server pick up the new code automatically on their next invocation — no clawmem setup hooks re-run needed. The hook command in ~/.claude/settings.json is ${binPath} hook ${name}, which resolves at runtime to the upgraded binary.
entity_triples actually populates now. The decision-extractor Stop hook persists observer-emitted SPO triples into entity_triples using canonical vault:type:slug entity IDs shared with A-MEM. Eligible observation types are decision, preference, milestone, problem, discovery, feature.kg_query accepts canonical IDs as well as entity names. Callers that already resolved an entity via searchEntities or list_vaults output can now round-trip the canonical ID (e.g. default:project:clawmem) directly into kg_query without going through a name-based fallback that would fabricate a different ID.adopted, migrated_to, deployed_to, runs_on, replaced, depends_on, integrates_with, uses, prefers, avoids, caused_by, resolved_by, owned_by are emitted. Anything the observer produces outside this set is silently dropped at parse time.obsHash slice so each observation gets a unique document row. Pre-v0.8.5 sessions silently lost the second-onward observation per type.Optional. Pre-v0.8.5 runs left two kinds of harmless dead data in SQLite:
entity_nodes rows with entity_type='auto' — minted by the old regex-based triple path. 'auto' is not a valid compatibility bucket, so these entities never resolve via kg_query — they cost a few KB of storage and nothing else.entity_triples rows with schema-placeholder source_fact values (e.g. "Individual atomic fact", "canonical entity name") — the 1.7B observer occasionally echoed example text from the old prompt into real facts.Neither affects correctness or query results going forward, because v0.8.5 writes canonical IDs only and the new prompt + parser filter placeholder strings before persistence. Clean them if you want a tidy store:
sqlite3 ~/.cache/clawmem/index.sqlite "
DELETE FROM entity_triples WHERE source_fact LIKE '%atomic fact%' OR source_fact LIKE '%canonical entity name%';
DELETE FROM entity_nodes WHERE entity_type='auto';
"
The troubleshooting guide has the full set of diagnostic queries and the symptom-by-symptom checklist for confirming you were on pre-v0.8.5: see the “kg_query returns empty for every entity” entry in docs/troubleshooting.md.
clawmem reindex --enrich?No. v0.8.5 does not introduce new enrichment stages, so --enrich is not required to benefit from the fix. New Stop-hook activity from v0.8.5 onward is the cleanest source of triples.
Running --enrich anyway is harmless but unnecessary — it will re-extract entities against the same entity cap as v0.8.3, not re-fire the decision-extractor hook. Past observation transcripts are gone (they were consumed by the Stop hook on their original session), so re-enrichment on already-persisted _clawmem/observations/*.md files cannot recover observations lost to the pre-v0.8.5 path-collision bug — those are permanently gone. Only future sessions generate new triples.
After a real Claude Code session that fires the Stop hook:
# Should show reconstructed "subject predicate object" strings, not placeholder echoes or JSON blobs
sqlite3 ~/.cache/clawmem/index.sqlite \
"SELECT source_fact FROM entity_triples ORDER BY created_at DESC LIMIT 5;"
# Should show only real bucket types (project/service/tool/concept/person/org/location), never 'auto'
sqlite3 ~/.cache/clawmem/index.sqlite \
"SELECT DISTINCT entity_type FROM entity_nodes
WHERE entity_id IN (SELECT subject_id FROM entity_triples ORDER BY created_at DESC LIMIT 50);"
# Should increment after each real session — not after every indexing tick
sqlite3 ~/.cache/clawmem/index.sqlite "SELECT COUNT(*) FROM entity_triples;"
If entity_triples still shows zero after multiple Stop-hook-firing sessions, the Stop hook itself is not firing — check ~/.claude/settings.json for a ClawMem entry under hooks.Stop, and run clawmem doctor to verify hook installation.
v0.8.3 is a drop-in patch release. No schema changes, no new env vars, no new dependencies. All changes are transparent behavior fixes that take effect automatically after upgrading the package and restarting any long-lived clawmem processes.
bun update -g clawmem # or: npm update -g clawmem
# Or source install:
cd ~/clawmem && git pull
# Restart long-lived daemons so they pick up the new entity extraction + self-loop guard
systemctl --user restart clawmem-watcher.service
content_type: research keep up to 15 entities per enrichment pass (was 10), hub and conversation documents keep up to 12, and short types (decision, deductive, note, handoff, progress) tighten to 8. Anything else — including documents with no content_type frontmatter — keeps the pre-v0.8.3 default of 10. The LLM extraction prompt advertises the dynamic cap to the model directly, so a compliant model no longer stops early on long-form documents.memory_relations are rejected. The insertRelation API boundary silently drops writes where fromDoc === toDoc, and the beads dependency bridge applies the same filter. No existing caller was known to emit self-loops — this is a defensive guard, not a bug fix for an observed failure.clawmem reindex --enrich to pick up the new entity cap?Only if you want previously-enriched long-form documents to re-extract entities against the new cap. A-MEM enrichment is tied to an input_hash of (title + body), so re-enrichment is skipped when the document content is unchanged. To force re-extraction against the new cap on already-indexed docs:
# Re-enrich all documents (LLM call per doc — expect latency on large vaults)
clawmem reindex --enrich
This is purely opt-in. New and modified documents pick up the new cap automatically on their next enrichment pass — no manual step needed for those.
No. The self-loop guard only affects new writes. Existing self-loops in memory_relations (if any) are not scrubbed on upgrade. To check for and clean pre-existing self-loops:
sqlite3 ~/.cache/clawmem/index.sqlite \
"SELECT COUNT(*) FROM memory_relations WHERE source_id = target_id;"
# If non-zero and you want to clean them:
sqlite3 ~/.cache/clawmem/index.sqlite \
"DELETE FROM memory_relations WHERE source_id = target_id;"
This is optional housekeeping — the guard ensures no new self-loops are created, and existing ones have no observed impact beyond graph noise.
v0.8.2 is a pure code release: no schema changes, no new dependencies, no new env vars. The only behavior change is operational — the long-lived clawmem watch process now hosts the consolidation and heavy maintenance lane workers in addition to cmdMcp, and the light lane gained the same DB-backed worker_leases exclusivity the heavy lane already had. See docs/concepts/architecture.md#dual-host-worker-architecture-v082 for the architectural walkthrough.
git pull # or: bun update -g clawmem / npm update -g clawmem
systemctl --user restart clawmem-watcher.service # if installed as a user unit
Move worker hosting from cmdMcp (per-session) to cmdWatch (long-lived, canonical) by setting the env vars on your watcher service unit instead of the wrapper. Example systemd drop-in:
systemctl --user edit clawmem-watcher.service
Then paste:
[Service]
Environment=CLAWMEM_ENABLE_CONSOLIDATION=true
Environment=CLAWMEM_HEAVY_LANE=true
Environment=CLAWMEM_HEAVY_LANE_WINDOW_START=2
Environment=CLAWMEM_HEAVY_LANE_WINDOW_END=6
Then systemctl --user restart clawmem-watcher.service. The watcher process now runs both lanes 24/7 — the heavy lane sees the configured 02:00-06:00 quiet window every night regardless of whether any Claude Code session is open at the time, and the light lane drains the enrichment backlog continuously.
cmdMcp remains a supported fallback host for users who do not run clawmem watch (e.g. macOS users running everything via Claude Code launchd). When CLAWMEM_HEAVY_LANE=true is set on a stdio MCP host, cmdMcp emits a one-line warning to stderr advising operators to move heavy-lane hosting to the watcher.
light-consolidation key) — runConsolidationTick now wraps each tick in withWorkerLease. Two host processes against the same vault cannot race on Phase 2 consolidated_observations writes or duplicate Phase 3 deductive synthesis LLM calls. The in-process isRunning reentrancy guard remains as the cheap first defense before the SQLite round-trip.cmdWatch hosts both workers — same env-var gates as cmdMcp. Off by default in both hosts.cmdMcp heavy-lane warning — console.error advises moving heavy-lane hosting to the watcher.stopConsolidationWorker and the closure returned by startHeavyMaintenanceWorker are now async. They clear their setInterval AND poll their in-flight running flag until any mid-tick worker drains before resolving, so the worker’s withWorkerLease finally block runs against a still-open store. Bounded waits (15s light, 30s heavy) prevent stuck ticks from wedging shutdown.cmdWatch and cmdMcp register SIGINT/SIGTERM handlers before any worker initialization, eliminating the brief race window where a signal arriving mid-startup would terminate via the default action and skip the async drain.Running BOTH clawmem watch (with env vars) AND a per-session clawmem mcp (with env vars) against the same vault is supported in v0.8.2. The worker_leases table arbitrates: only one host wins each tick, the other journals a skip (heavy lane) or logs “lease held” (light lane). For the cleanest setup, set the env vars on clawmem-watcher.service only and leave cmdMcp unset.
clawmem embed (no embedding contract change)clawmem reindex (no document storage change)clawmem setup hooks (no hook config change)bun install / npm install (no dependency change)# Confirm watcher is running latest code
systemctl --user status clawmem-watcher.service
# After enabling the env vars + restart, watcher should log both worker
# startup banners. The exact intervals depend on the env vars you set —
# defaults are 5-min light lane and 30-min heavy lane.
journalctl --user -u clawmem-watcher.service -n 50 --no-pager | \
grep -E "Starting (consolidation|heavy)"
# Expected:
# [watch] Starting consolidation worker (light lane, interval=...)
# [consolidation] Worker started
# [watch] Starting heavy maintenance lane worker
# [heavy-lane] Starting worker (interval=..., window=..., ...)
For the full operator guide — what to expect over the first hour, the per-usage-pattern tuning matrix, the complete monitoring query set, and rollback steps — see docs/guides/systemd-services.md.
| Version | Change | Tables / columns added |
|---|---|---|
| v0.7.1 | Contradiction gate | memory_relations.contradict_confidence |
| v0.8.0 | Heavy maintenance lane | maintenance_runs + worker_leases tables |
| v0.8.1 | Multi-turn lookback | context_usage.query_text |
Applied on first open of any v0.7.1+ process against the vault. No action required.
contradicts taxonomy cleanupThe v0.7.1 P0 taxonomy cleanup standardized on the A-MEM plural form contradicts across the codebase. Prior code mixed contradict and contradicts, so v0.7.0 vaults may contain orphaned rows with relation_type = 'contradict' (singular) that v0.7.1+ queries cannot reach.
Check whether your vault has any:
sqlite3 -readonly ~/.cache/clawmem/index.sqlite \
"SELECT relation_type, COUNT(*) FROM memory_relations \
WHERE relation_type LIKE 'contradict%' GROUP BY relation_type"
If the output shows only contradicts, nothing to do. If it shows a contradict (singular) row, rescue them:
sqlite3 ~/.cache/clawmem/index.sqlite \
"UPDATE memory_relations SET relation_type='contradicts' \
WHERE relation_type='contradict'"
Cosmetic cleanup — orphaned rows are harmless but invisible to contradict-aware features (the merge-time contradiction gate, Phase 3 deductive dedupe linking, and intent_search WHY-graph traversal over contradicts edges).
None of these auto-enable. They are new capabilities gated behind environment variables on the long-lived clawmem process.
| Feature | Version | How to enable |
|---|---|---|
| Heavy maintenance lane | v0.8.0 | CLAWMEM_HEAVY_LANE=true + CLAWMEM_HEAVY_LANE_WINDOW_START/END for quiet hours. Second consolidation worker gated by query-rate, scoped exclusively via DB-backed worker_leases, stale-first batching, journaled in maintenance_runs. |
| Surprisal selector | v0.8.0 | CLAWMEM_HEAVY_LANE_SURPRISAL=true. Seeds Phase 2 with k-NN anomaly-ranked doc ids; falls back to stale-first (surprisal-fallback-stale metric) on vaults without embeddings. |
| Post-import conversation synthesis | v0.7.2 | clawmem mine <dir> --synthesize flag. One-shot, not persistent. Runs a two-pass LLM pipeline over freshly imported conversation docs to extract structured decision / preference / milestone / problem facts with cross-fact relations. |
| Consolidation worker | v0.7.1 | CLAWMEM_ENABLE_CONSOLIDATION=true (flag exists pre-v0.7.1). v0.7.1 attaches the new safety gates (Ext 1/2/3) to the existing Phase 2/3 path — enabling the flag picks up the gates automatically. |
| Contradiction policy | v0.7.1 | CLAWMEM_CONTRADICTION_POLICY=link (default, keep both rows + insert contradicts edge) or supersede (mark old row status='inactive'). |
| Merge guard dry-run | v0.7.1 | CLAWMEM_MERGE_GUARD_DRY_RUN=true logs merge-safety rejections without enforcing — useful for calibration on older vaults before flipping the gate on. Leave false (default) to enforce. |
Full environment variable reference: docs/reference/cli.md.
These activate automatically as soon as the new code runs:
<instruction> and <relationships> blocks appear inside <vault-context> when memory-graph edges exist between surfaced docsquery_text on each prompt and uses up to 2 recent same-session priors (≤10 min old, ≤2000 chars total) for discovery queries only (NOT for rerank, composite scoring, or snippet extraction)CLAWMEM_ENABLE_CONSOLIDATION or CLAWMEM_HEAVY_LANE)# Schema check — confirms v0.7.1 / v0.8.0 / v0.8.1 migrations applied
sqlite3 -readonly ~/.cache/clawmem/index.sqlite \
"SELECT name FROM sqlite_master WHERE type='table' AND name IN \
('maintenance_runs','worker_leases','recall_stats','recall_events')"
# expect: all four tables listed
sqlite3 -readonly ~/.cache/clawmem/index.sqlite "PRAGMA table_info(context_usage)" \
| grep query_text
# expect: a row with query_text TEXT
sqlite3 -readonly ~/.cache/clawmem/index.sqlite "PRAGMA table_info(memory_relations)" \
| grep contradict_confidence
# expect: a row with contradict_confidence REAL
# Full health check
clawmem doctor
Five independent gates around consolidation and context-surfacing:
contradicts plural)link / supersede policy)<vault-context>See docs/concepts/architecture.md for the architectural walkthrough.
Two-pass LLM pipeline over freshly imported content_type='conversation' docs. Pass 1 extracts structured facts; Pass 2 resolves cross-fact links via a local alias map with SQL fallback. Gated behind clawmem mine <dir> --synthesize. Idempotent on reruns.
Second consolidation worker gated by a configurable quiet-hour window and query-rate, scoped exclusively via DB-backed worker_leases with atomic INSERT ... ON CONFLICT DO UPDATE ... WHERE expires_at <= ? acquisition and 16-byte fencing tokens. Journals every attempt (including skips) in maintenance_runs. Stale-first batching by default; optional surprisal selector degrades gracefully on vaults without embeddings.
See docs/concepts/architecture.md for the architectural walkthrough.
Context-surfacing hook joins the current prompt with up to 2 recent same-session priors from the new context_usage.query_text column for discovery queries (vector, FTS, expansion). Rerank, composite scoring, chunk selection, snippet extraction, file-path FTS supplements, and recall attribution all stay on the raw current prompt. Privacy-conscious persistence split: gated skip paths (slash commands, heartbeats, too-short prompts) persist query_text = NULL to keep agent noise out of future lookback; post-retrieval empty paths still persist so a follow-up turn can reuse the intent.
See docs/concepts/architecture.md for the architectural walkthrough.
The knowledge graph finally populates. Pre-v0.8.5 decision-extractor wrote zero rows to entity_triples on production vaults because of a bug cluster: observation-type gate too narrow (rejected ~77% of real observations), regex-based triple extractor expected sentence shape (facts are usually descriptive phrases), entity IDs written with invalid 'auto' type, same-type observations in one session collided on filename and the second was silently dropped, and a weak model occasionally echoed schema placeholder text ("Individual atomic fact") into real triples. v0.8.5 replaces the regex path with observer-LLM-emitted <triples> blocks using a tight 13-predicate vocabulary (adopted, migrated_to, deployed_to, runs_on, replaced, depends_on, integrates_with, uses, prefers, avoids, caused_by, resolved_by, owned_by), canonical vault:type:slug entity IDs via ensureEntityCanonical (shared namespace with A-MEM, never writes 'auto'), ambiguity-safe type inheritance via resolveEntityTypeExact (zero or multiple bucket matches → default to concept), widened observation-type gate (decision/preference/milestone/problem/discovery/feature), 8-char SHA256 hash slice in observation paths for collision-free persistence, placeholder defense at both prompt and parser, kg_query canonical-ID round-trip, and source_doc_id provenance on every triple. 4-turn Codex review.
See docs/troubleshooting.md “kg_query returns empty for every entity” for diagnostic symptoms and optional cleanup SQL.
Schema migrations are additive — downgrading to an earlier version leaves new tables and columns in place, and the older code simply does not touch them. No data corruption risk.
cd ~/clawmem
git checkout v0.7.0 # or your target tag
systemctl --user restart clawmem-watcher.service
To fully remove v0.7.1+ schema additions (destructive, loses any v0.7.1+ data written to the new tables):
sqlite3 ~/.cache/clawmem/index.sqlite << 'SQL'
DROP TABLE IF EXISTS maintenance_runs;
DROP TABLE IF EXISTS worker_leases;
SQL
context_usage.query_text and memory_relations.contradict_confidence cannot be removed without rebuilding the table in SQLite. They are nullable and cost nothing to keep. Not recommended.