Bug: CR-2 guard fires on every L1 write in OpenClaw standalone mode
What I see
In the OpenClaw host-adapter path (the in-process plugin install, not the standalone HTTP gateway), every L1 extraction emits this warning 4 times per session (once per memory written):
[memory-tdai][l1-writer] [CR-2 guard] writeMemory called without storage adapter; falling back to local fs at <dataDir>/records/2026-06-13.jsonl. In service mode this means JSONL is written to ephemeral pod fs and will be lost on restart. Caller must pass 'storage' to writeMemory.
It is reproducible on a fresh single-host install of memory-tencentdb v1.0.0 in OpenClaw. The warnings appear on every successful L1 batch, not on failures.
What I think is happening
The CR-2 guard at src/core/record/l1-writer.ts:202-217 was added in the 2026-05-19 fix to make a silent data-loss bug visible. The guard's own comment says standalone mode is supposed to be benign because "the gateway auto-wires a LocalStorageBackend at startup (server.ts:199-203)".
When I trace the OpenClaw path, I see the auto-wire is missing:
src/gateway/server.ts:261 constructs new TdaiCore({ hostAdapter, config, sessionFilter }) — no storage.
- The OpenClaw plugin entry at the bundled
dist/index.mjs:21372 (the OpenClawHostAdapter host) does the same — new TdaiCore({ hostAdapter, config: cfg, sessionFilter }).
TdaiCore.constructor at src/core/tdai-core.ts:139 sets this.storage = opts.storage and never falls back, so this.storage is undefined.
- The L1/L2/L3 runners are constructed with
storage: this.storage (src/core/tdai-core.ts:525, 541, 556) and forward undefined to extractL1Memories, which forwards it to writeMemory.
writeMemory enters the else branch and emits the CR-2 warning.
So the warning is correct, but the auto-wire it depends on is not happening for the OpenClaw host-adapter entry — the comment in l1-writer.ts documents an intent that is not in the current code.
The standalone HTTP gateway at server.ts:261 has the same shape, but the same server.ts file does construct a LocalStorageBackend at lines 362, 558, and 1443, just not at the constructor at 261. So the auto-wire is half-implemented.
Impact
- Standalone mode (single host): noisy logs, but data still lands at
<dataDir>/records/<date>.jsonl because of the else branch's fs fallback. No actual data loss today, but the warning is misleading — it warns about a data-loss risk that is not real in this configuration.
- Service mode (pod with a
CosStorageBackend): the caller is expected to pass storage to TdaiCore. If they forget, the warning fires and data goes to ephemeral pod fs and is lost on restart. This is the real data-loss path the CR-2 fix was meant to catch, and it is still real.
The current code conflates the two cases in its warning text, which makes the warning un-actionable for the standalone operator.
Expected
The CR-2 guard should not fire in standalone mode. Either:
- Restore the auto-wire the guard comment promises: in
TdaiCore.constructor, default this.storage to new LocalStorageBackend({ rootDir: this.dataDir, logger: this.logger }) when opts.storage is undefined. Service-mode callers that pass a CosStorageBackend still win because the fallback only fires when undefined. This is a one-line change in the constructor and matches the documented intent.
- Or, change the warning text so it no longer claims the auto-wire is happening when it is not. Lower-quality fix, since the CR-2 root cause is still latent for service-mode callers who forget to pass
storage.
I prefer (1). It is closer to the original design intent, makes the guard actually fire only for the real bug (service mode with missing storage), and removes the noisy logs in standalone mode.
Reproduction
Fresh OpenClaw install, memory-tencentdb v1.0.0 (npm @tencentdb-agent-memory/memory-tencentdb latest). Open a session, send a few turns, wait for an L1 extraction round. Warnings appear in journalctl --user -u openclaw-gateway.service. The L1 records themselves do land at ~/.openclaw/memory-tdai/records/<date>.jsonl, so the data is safe; only the warning text is wrong.
Proposed fix
In src/core/tdai-core.ts:
import { LocalStorageBackend } from "./storage/local-backend.js";
// ...
constructor(opts: TdaiCoreOptions) {
// ...
// CR-2 (2026-05-19): restore the auto-wire for standalone mode that the
// l1-writer guard comment refers to. The guard expects every writeMemory
// call to be backed by a StorageAdapter. Service-mode callers pass one
// explicitly (e.g. CosStorageBackend). Standalone installs omit it and
// rely on the gateway to fall back to local fs. We restore that
// auto-wire here. Custom-storage callers still win because the fallback
// only fires when opts.storage is undefined.
this.storage = opts.storage ?? new LocalStorageBackend({ rootDir: this.dataDir, logger: this.logger });
}
I am happy to send a PR with this change if maintainers agree. I have already tested it locally on v1.0.0 / OpenClaw — CR-2 warnings stop, L1 writes still land at the same JSONL path (the LocalStorageBackend wraps the same fs append with O_APPEND, so the on-disk format is identical).
Environment
memory-tencentdb v1.0.0 (npm)
- OpenClaw v2026.5.20
- Node 22.22.0
- Host: WSL2 Ubuntu, single-host install, OpenClaw host-adapter path
Bug: CR-2 guard fires on every L1 write in OpenClaw standalone mode
What I see
In the OpenClaw host-adapter path (the in-process plugin install, not the standalone HTTP gateway), every L1 extraction emits this warning 4 times per session (once per memory written):
It is reproducible on a fresh single-host install of
memory-tencentdbv1.0.0 in OpenClaw. The warnings appear on every successful L1 batch, not on failures.What I think is happening
The CR-2 guard at
src/core/record/l1-writer.ts:202-217was added in the 2026-05-19 fix to make a silent data-loss bug visible. The guard's own comment says standalone mode is supposed to be benign because "the gateway auto-wires aLocalStorageBackendat startup (server.ts:199-203)".When I trace the OpenClaw path, I see the auto-wire is missing:
src/gateway/server.ts:261constructsnew TdaiCore({ hostAdapter, config, sessionFilter })— nostorage.dist/index.mjs:21372(theOpenClawHostAdapterhost) does the same —new TdaiCore({ hostAdapter, config: cfg, sessionFilter }).TdaiCore.constructoratsrc/core/tdai-core.ts:139setsthis.storage = opts.storageand never falls back, sothis.storageisundefined.storage: this.storage(src/core/tdai-core.ts:525, 541, 556) and forwardundefinedtoextractL1Memories, which forwards it towriteMemory.writeMemoryenters theelsebranch and emits the CR-2 warning.So the warning is correct, but the auto-wire it depends on is not happening for the OpenClaw host-adapter entry — the comment in
l1-writer.tsdocuments an intent that is not in the current code.The standalone HTTP gateway at
server.ts:261has the same shape, but the sameserver.tsfile does construct aLocalStorageBackendat lines 362, 558, and 1443, just not at the constructor at 261. So the auto-wire is half-implemented.Impact
<dataDir>/records/<date>.jsonlbecause of theelsebranch's fs fallback. No actual data loss today, but the warning is misleading — it warns about a data-loss risk that is not real in this configuration.CosStorageBackend): the caller is expected to passstoragetoTdaiCore. If they forget, the warning fires and data goes to ephemeral pod fs and is lost on restart. This is the real data-loss path the CR-2 fix was meant to catch, and it is still real.The current code conflates the two cases in its warning text, which makes the warning un-actionable for the standalone operator.
Expected
The CR-2 guard should not fire in standalone mode. Either:
TdaiCore.constructor, defaultthis.storagetonew LocalStorageBackend({ rootDir: this.dataDir, logger: this.logger })whenopts.storageis undefined. Service-mode callers that pass aCosStorageBackendstill win because the fallback only fires when undefined. This is a one-line change in the constructor and matches the documented intent.storage.I prefer (1). It is closer to the original design intent, makes the guard actually fire only for the real bug (service mode with missing
storage), and removes the noisy logs in standalone mode.Reproduction
Fresh OpenClaw install,
memory-tencentdbv1.0.0 (npm@tencentdb-agent-memory/memory-tencentdblatest). Open a session, send a few turns, wait for an L1 extraction round. Warnings appear injournalctl --user -u openclaw-gateway.service. The L1 records themselves do land at~/.openclaw/memory-tdai/records/<date>.jsonl, so the data is safe; only the warning text is wrong.Proposed fix
In
src/core/tdai-core.ts:I am happy to send a PR with this change if maintainers agree. I have already tested it locally on v1.0.0 / OpenClaw — CR-2 warnings stop, L1 writes still land at the same JSONL path (the
LocalStorageBackendwraps the same fs append withO_APPEND, so the on-disk format is identical).Environment
memory-tencentdbv1.0.0 (npm)