Summary
Manual memory_recall waits for one access-metadata write per returned result before returning retrieval output that is already available. Those writes acquire the shared store write lock, so lock contention or an index fold can make an otherwise successful recall exceed the host tool timeout.
Auto-recall had the same problem and moved Tier 1 metadata patching off its critical path in #806. Manual recall still awaits Promise.allSettled(...patchMetadata()) in src/tools.ts.
Expected behavior
Once retrieval succeeds, memory_recall should return its results without waiting for best-effort access telemetry. The metadata patches should still start in the background, preserve the existing governance fields and scope filter, and report failures without changing the successful recall result.
Reproduction
- Use current
master with an active LanceDB store.
- Create write contention through concurrent capture/update activity or an index fold.
- Run manual
memory_recall with multiple results.
- Observe that the tool can remain pending on
patchMetadata() after retrieval has completed.
A deterministic unit reproduction is to make store.patchMetadata() return an unresolved promise. The current manual recall tool does not return until that promise settles; the equivalent auto-recall path already returns context before its background metadata patches settle.
Observed evidence
- OpenClaw:
2026.6.11
- Plugin: current
master at 7af97d4
- Node.js:
24.18.0
- Platform: macOS arm64
- Retrieval-only debug pipeline: under 1 second
- Manual recall: hit OpenClaw's 90-second dynamic-tool watchdog during contention
- A recent write-threshold index fold on the same store took about 37 seconds
- A single-result manual recall completed normally after contention cleared
The file-lock retry budget can exceed the host's 90-second tool watchdog, so increasing the tool timeout would only mask the coupling.
Suggested fix
Match #806: run the existing manual-recall metadata patches as best-effort background work with rejection logging, while preserving the exact metadata patch and resolved scopeFilter. Add regression coverage proving stalled metadata writes do not delay the recall payload and rejected background patches remain observable.
Index folding from #887 is still useful and should remain enabled; the problem is optional metadata writes being part of the user-facing read path.
Summary
Manual
memory_recallwaits for one access-metadata write per returned result before returning retrieval output that is already available. Those writes acquire the shared store write lock, so lock contention or an index fold can make an otherwise successful recall exceed the host tool timeout.Auto-recall had the same problem and moved Tier 1 metadata patching off its critical path in #806. Manual recall still awaits
Promise.allSettled(...patchMetadata())insrc/tools.ts.Expected behavior
Once retrieval succeeds,
memory_recallshould return its results without waiting for best-effort access telemetry. The metadata patches should still start in the background, preserve the existing governance fields and scope filter, and report failures without changing the successful recall result.Reproduction
masterwith an active LanceDB store.memory_recallwith multiple results.patchMetadata()after retrieval has completed.A deterministic unit reproduction is to make
store.patchMetadata()return an unresolved promise. The current manual recall tool does not return until that promise settles; the equivalent auto-recall path already returns context before its background metadata patches settle.Observed evidence
2026.6.11masterat7af97d424.18.0The file-lock retry budget can exceed the host's 90-second tool watchdog, so increasing the tool timeout would only mask the coupling.
Suggested fix
Match #806: run the existing manual-recall metadata patches as best-effort background work with rejection logging, while preserving the exact metadata patch and resolved
scopeFilter. Add regression coverage proving stalled metadata writes do not delay the recall payload and rejected background patches remain observable.Index folding from #887 is still useful and should remain enabled; the problem is optional metadata writes being part of the user-facing read path.