Skip to content

memory_recall blocks on best-effort access metadata writes #925

Description

@TurboTheTurtle

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

  1. Use current master with an active LanceDB store.
  2. Create write contention through concurrent capture/update activity or an index fold.
  3. Run manual memory_recall with multiple results.
  4. 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions