Skip to content

fix(memory): self-heal missing index identity by initializing provider during sync#91897

Merged
vincentkoc merged 4 commits into
openclaw:mainfrom
xydt-tanshanshan:fix/memory-self-heal-missing-identity-91167
Jun 11, 2026
Merged

fix(memory): self-heal missing index identity by initializing provider during sync#91897
vincentkoc merged 4 commits into
openclaw:mainfrom
xydt-tanshanshan:fix/memory-self-heal-missing-identity-91167

Conversation

@xydt-tanshanshan

@xydt-tanshanshan xydt-tanshanshan commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

When a gateway's memory index identity is "missing" with chunks already indexed, the gateway never self-heals on its own if the embedding provider is unavailable. canRebuildMissingIdentity stays false because this.provider === null, so needsMissingIdentityReindex is false, and the sync loop bails out with dirty=true forever.

The real fix: when every existing chunk has model='fts-only', rebuilding the index as FTS-only is safe — no semantic data is lost. So canRebuildMissingIdentity should also be true when hasOnlyFtsChunks, even if the provider is unavailable.

A previous approach (calling ensureProviderInitialized() inside runSync()) was rejected by Codex review as redundant because the public sync() method already initializes the provider before runSyncWithReadonlyRecovery(). This revision targets the actual missing-identity reindex decision directly.

This addresses the FTS-only chunk portion of #91167. The broader stale-DB-handle problem (CLI atomic swap not reaching the running gateway) and the semantic-chunk-with-unavailable-provider scenario remain outside this diff.

Change Type

  • Bug fix

Scope

  • Memory / storage

Linked Issue

Motivation

Issue #91167 reports that a long-running gateway whose memory index-identity becomes "missing" with chunks already indexed never recovers on its own. When the embedding provider is unavailable (unregistered plugin, auth failure, network error), canRebuildMissingIdentity = this.provider !== null || !this.settings.provider || this.settings.provider === "none" evaluates to false, so needsMissingIdentityReindex = false, and the sync loop returns early with dirty=true forever.

However, when every existing chunk has model='fts-only', there is no semantic vector data at risk. Rebuilding the index as FTS-only restores the metadata without losing any information. The current guard is too conservative for this case.

Changes

File: extensions/memory-core/src/memory/manager-sync-ops.ts

  1. Added hasSemanticChunks() helper (line 400): queries SELECT 1 FROM chunks WHERE model != 'fts-only' LIMIT 1 to detect whether any chunks have a non-FTS-only model.

  2. Modified canRebuildMissingIdentity logic (line 2080): added || hasOnlyFtsChunks where hasOnlyFtsChunks = this.hasIndexedChunks() && !this.hasSemanticChunks(). When all indexed chunks are FTS-only, rebuilding as FTS-only is safe even without a provider.

This is a narrow additive change: one new protected method + one additional boolean condition. No config, default, DB handle, or provider-init changes. The assertFtsOnlySyncAllowed() guard at line 2043 already allows sync when existingMeta === null (line 2018), so no additional guard changes are needed.

File: extensions/memory-core/src/memory/manager.self-heal-missing-identity.test.ts (new)

1 test covering the non-forced self-heal path: seeds FTS-only chunks with no meta, creates a manager with provider: "auto" and provider: null (unavailable), syncs without force, verifies that indexIdentity.status transitions from "missing" to "valid" and dirty becomes false.

Real Behavior Proof

  • Behavior addressed: A gateway whose memory index identity is "missing" with FTS-only chunks already indexed never self-heals when the embedding provider is unavailable, because canRebuildMissingIdentity stays false and the sync loop bails out with dirty=true forever.
  • Real environment tested: Linux (RHEL), Node.js 22.22, local checkout on branch fix/memory-self-heal-missing-identity-91167 at commit 7621e7d04, dev build via node scripts/run-node.mjs.
  • Exact steps or command run after this patch: Seed 1 FTS-only chunk row into ~/.openclaw/memory/main.sqlite with no memory_index_meta_v1 row (simulating bug(memory): gateway cannot self-heal a missing index identity when chunks are already indexed #91167's "chunks exist but meta missing" state), then run node scripts/run-node.mjs memory status (non-forced, simulating gateway periodic sync)
  • Evidence after fix: Unit test output proving completed self-heal with non-forced sync, non-none configured provider unavailable, and FTS-only chunks. The test seeds 1 FTS-only chunk with no meta, configures provider: "auto" with provider: null (unavailable), syncs without force, and verifies identity transitions from "missing" to "valid" and dirty becomes false:
$ node scripts/run-vitest.mjs extensions/memory-core/src/memory/manager.self-heal-missing-identity.test.ts

 ✓ self-heals missing identity on non-forced gateway sync when all chunks are FTS-only and provider is unavailable (183ms)

 Test Files  1 passed (1)
      Tests  1 passed (1)

Live output with provider: "none" (already-supported path, confirms the rebuild mechanism works end-to-end):

Memory Search (main)
Provider: none (requested: none)
Indexed: 50/50 files · 697 chunks
Dirty: no
Embeddings: unavailable
Embeddings error: No embedding provider available (FTS-only mode)
FTS: ready

SQLite meta after self-heal:

$ sqlite3 ~/.openclaw/memory/main.sqlite "SELECT value FROM meta WHERE key='memory_index_meta_v1'"
{"model":"fts-only","provider":"none","sources":["memory"],...}

Live output with provider: "auto" → resolved to "openai" → unavailable (Invalid URL), confirming the new hasOnlyFtsChunks branch activates when a non-none provider is configured but unavailable:

Memory Search (main)
Provider: openai (requested: openai)
Indexed: 1/50 files · 1 chunks
Embeddings: unavailable
Embeddings error: Invalid URL protocol: the URL must start with `http:` or `https:`.
Index identity: index metadata is missing

Note: The live run with a non-none unavailable provider did not complete self-heal in this run because the CLI memory status --index uses purpose: "cli" which triggers needsExplicitIdentityReindex, and the full reindex requires a working embedding endpoint. The hasOnlyFtsChunks path correctly sets needsMissingIdentityReindex = true and needsFullReindex = true, but the subsequent reindex attempts to re-embed files and hits the provider error. This is expected: the gateway's periodic non-forced sync would trigger the same path but the FTS-only rebuild (without embedding) should succeed since no semantic chunks exist. The unit test proves the identity decision works correctly; the live embedding failure is an environment limitation.

Note on proof dependency: A live run proving end-to-end self-heal with a non-none unavailable provider requires PR #91862 (unknown provider degradation fix) to be merged first, so that the CLI does not crash when the provider is unregistered. Once #91862 is merged, the live proof can be refreshed.

  • Observed result after fix: When every indexed chunk has model='fts-only', canRebuildMissingIdentity now includes hasOnlyFtsChunks=true, allowing the self-heal reindex path to activate on a non-forced gateway periodic sync. The index identity transitions from "missing" to "valid" without requiring a provider. Semantic chunks (model != 'fts-only') are still protected: if any semantic chunk exists, hasOnlyFtsChunks=false and canRebuildMissingIdentity requires the provider to be available.
  • What was not tested: The semantic-chunk scenario (chunks with embedding vectors but provider unavailable) — this is intentionally not fixed here because silently downgrading semantic chunks to FTS-only would lose data. The stale DB handle problem (CLI atomic swap not reaching the running gateway) remains a follow-up.

Risks

  • Low risk: The fix only relaxes the guard for a specific safe case — when all existing chunks are FTS-only. No semantic data can be lost because there are no semantic chunks to downgrade.
  • Additive: hasSemanticChunks() is a read-only query that does not modify any state. The hasOnlyFtsChunks condition only adds one more true case to canRebuildMissingIdentity; all existing conditions remain unchanged.
  • Protected: When any chunk has a non-FTS-only model, hasSemanticChunks()=truehasOnlyFtsChunks=falsecanRebuildMissingIdentity still requires the provider. This prevents silent downgrade of semantic vector indexes.
  • Scope limitation: This PR only addresses the FTS-only chunk self-heal. The broader stale-DB-handle and semantic-chunk-with-unavailable-provider scenarios remain outside this diff.

AI Assistance 🤖

  • AI-assisted: Yes
  • Human confirmed understanding: Yes
  • AI tools: iCodemate (analysis, code implementation, test writing, Codex review revision)

@openclaw-barnacle openclaw-barnacle Bot added extensions: memory-core Extension: memory-core size: S proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 10, 2026
@clawsweeper

clawsweeper Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 10, 2026, 5:37 AM ET / 09:37 UTC.

Summary
The PR adds a memory-core chunk-model check so missing index metadata can reindex FTS-only chunks without an embedding provider, plus a focused Vitest regression test.

PR surface: Source +22, Tests +140. Total +162 across 2 files.

Reproducibility: yes. from source: current main leaves missing identity dirty when chunks exist, the provider is unavailable, and the configured provider is not none. I did not execute tests because this is a read-only review.

Review metrics: none identified.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted terminal output or logs from a successful non-forced run where a configured non-none embedding provider is unavailable, all existing chunks are fts-only, identity becomes valid, and dirty becomes false.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR includes copied live output, but it uses provider: none, which current main already rebuilds; add redacted terminal output or logs for a configured non-none provider unavailable run and update the PR body to trigger fresh review.

Risk before merge

  • [P1] The remaining merge blocker is proof quality: the supplied live output uses the already-supported provider: none path, so maintainers still need redacted terminal output or logs showing non-forced self-heal with a configured non-none provider unavailable and all chunks FTS-only.

Maintainer options:

  1. Decide the mitigation before merge
    Land the narrow FTS-only missing-identity fix after proof shows the configured-provider-unavailable branch works; keep the stale DB handle and semantic-unavailable cases tracked on bug(memory): gateway cannot self-heal a missing index identity when chunks are already indexed #91167.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] No automated repair is appropriate because the remaining blocker is contributor-supplied real behavior proof, not a clear code defect.

Security
Cleared: The diff only changes local memory sync logic and adds a Vitest test; no concrete security or supply-chain concern was found.

Review details

Best possible solution:

Land the narrow FTS-only missing-identity fix after proof shows the configured-provider-unavailable branch works; keep the stale DB handle and semantic-unavailable cases tracked on #91167.

Do we have a high-confidence way to reproduce the issue?

Yes from source: current main leaves missing identity dirty when chunks exist, the provider is unavailable, and the configured provider is not none. I did not execute tests because this is a read-only review.

Is this the best way to solve the issue?

Yes for the scoped FTS-only slice: the change is in the existing missing-identity decision point and preserves semantic chunks. The proposed branch still needs proof for the actual configured-provider-unavailable scenario before merge.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against c84e52192063.

Label changes

Label justifications:

  • P1: The PR targets a memory-search recovery failure that can leave gateway memory unavailable until manual intervention or restart.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR includes copied live output, but it uses provider: none, which current main already rebuilds; add redacted terminal output or logs for a configured non-none provider unavailable run and update the PR body to trigger fresh review.
Evidence reviewed

PR surface:

Source +22, Tests +140. Total +162 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 24 2 +22
Tests 1 140 0 +140
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 164 2 +162

What I checked:

  • Root policy read: Read the full root repository policy and applied its OpenClaw PR review, extension-boundary, proof, and owner-history requirements. (AGENTS.md:1, c84e52192063)
  • Scoped extension policy read: Read the scoped extension policy; the diff stays within the bundled memory-core plugin and does not add public plugin API surface. (extensions/AGENTS.md:1, c84e52192063)
  • Current main missing-identity gate: Current main only rebuilds missing identity when a provider is present, no provider is configured, or the configured provider is none; a configured non-none provider with this.provider === null remains dirty and returns early. (extensions/memory-core/src/memory/manager-sync-ops.ts:2078, c84e52192063)
  • Provider initialization path checked: The public sync() method initializes the embedding provider before calling readonly recovery and runSync(), so the PR's current decision-point change is better targeted than reinitializing inside runSync(). (extensions/memory-core/src/memory/manager.ts:953, c84e52192063)
  • PR diff reviewed: The PR gates a new FTS-only chunk classification to missing identity, existing chunks, unavailable provider, and configured non-none provider, then includes that case in canRebuildMissingIdentity. (extensions/memory-core/src/memory/manager-sync-ops.ts:2081, 974fb8cf24e4)
  • Semantic protection checked: Existing coverage confirms missing semantic metadata is not rebuilt when embeddings are unavailable, and the PR's model scan preserves that invariant by requiring all chunks to be fts-only. (extensions/memory-core/src/memory/index.test.ts:900, c84e52192063)

Likely related people:

  • vincentkoc: Recent public history includes the safe missing-index-metadata rebuild behavior this PR extends, plus multiple memory-core commits and test adjustments in the same area. (role: recent area contributor; confidence: high; commits: d46dc39b18ec, e3ef136bca85, 24c39de9c143; files: extensions/memory-core/src/memory/manager-sync-ops.ts, extensions/memory-core/src/memory/index.test.ts)
  • osolmaz: Recent provider-local runtime and provider-unavailable changes touched the memory provider lifecycle that determines whether this branch runs without an embedding provider. (role: adjacent owner; confidence: medium; commits: 31371101678d; files: extensions/memory-core/src/memory/index.test.ts, extensions/memory-core/src/memory/manager.ts)
  • mushuiyu886: Recent batched memory embedding work changed the same sync and indexing paths that this PR relies on for source-wide reindex behavior. (role: recent area contributor; confidence: medium; commits: a36e05050a9d; files: extensions/memory-core/src/memory/manager-sync-ops.ts, extensions/memory-core/src/memory/manager-embedding-ops.ts, extensions/memory-core/src/memory/index.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. labels Jun 10, 2026
@xydt-tanshanshan

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jun 10, 2026
@xydt-tanshanshan

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@xydt-tanshanshan

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@vincentkoc vincentkoc self-assigned this Jun 11, 2026
xydt-tanshanshan and others added 4 commits June 11, 2026 09:48
…ovider during sync

When a gateway's memory index identity becomes "missing" with chunks
already indexed, canRebuildMissingIdentity stays false because
this.provider is null (async provider init hasn completed yet), so
needsMissingIdentityReindex is false, and the sync loop bails out
with dirty=true forever — the gateway never self-heals.

Now, when indexIdentity.status is "missing" and a provider is
configured but this.provider is null, runSync() calls
ensureProviderInitialized() first, then re-evaluates the identity
state. If the provider becomes available,
canRebuildMissingIdentity flips to true, unlocking the self-heal
reindex path.

Refs openclaw#91167
…ly and provider unavailable

When a gateway's memory index identity is 'missing' with chunks already
indexed, canRebuildMissingIdentity stays false if the embedding provider
is unavailable, causing the sync loop to bail out with dirty=true forever.

The previous approach (calling ensureProviderInitialized inside runSync)
was redundant because the public sync() method already initializes the
provider before runSyncWithReadonlyRecovery.

The real fix: when every existing chunk has model='fts-only', rebuilding
the index as FTS-only is safe — no semantic data is lost. So
canRebuildMissingIdentity should also be true when hasOnlyFtsChunks,
even if the provider is unavailable.

Also adds hasSemanticChunks() helper to detect whether any chunks have
a non-fts-only model.

Non-forced test: seeds FTS-only chunks with no meta, syncs without
force, verifies identity transitions from 'missing' to 'valid'.

Refs openclaw#91167
…h only

Only compute hasOnlyFtsChunks when identity is missing, chunks exist,
and the provider is unavailable. This avoids scanning the chunks table
for model classification on every ordinary sync.
@vincentkoc

Copy link
Copy Markdown
Member

Maintainer review complete. The self-heal remains fail-closed for semantic data: only a missing-identity index whose stored chunks are all fts-only can rebuild while the configured provider is unavailable.

Verification:

  • node scripts/run-vitest.mjs extensions/memory-core/src/memory/manager.self-heal-missing-identity.test.ts extensions/memory-core/src/memory/manager.fts-only-reindex.test.ts — 9 passed
  • added a negative regression proving semantic chunks retain missing identity and remain dirty rather than rebuilding
  • git diff --check
  • fresh Codex autoreview — clean, no actionable findings

GitHub CI on the updated SHA is the broad gate because local Blacksmith CLI and Crabbox broker auth are unavailable.

Updated head: 086e478

@vincentkoc
vincentkoc force-pushed the fix/memory-self-heal-missing-identity-91167 branch from 974fb8c to 086e478 Compare June 11, 2026 00:51
@vincentkoc
vincentkoc merged commit 1a2eb74 into openclaw:main Jun 11, 2026
9 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 11, 2026
…r during sync (openclaw#91897)

* [AI] fix(memory): self-heal missing index identity by initializing provider during sync

When a gateway's memory index identity becomes "missing" with chunks
already indexed, canRebuildMissingIdentity stays false because
this.provider is null (async provider init hasn completed yet), so
needsMissingIdentityReindex is false, and the sync loop bails out
with dirty=true forever — the gateway never self-heals.

Now, when indexIdentity.status is "missing" and a provider is
configured but this.provider is null, runSync() calls
ensureProviderInitialized() first, then re-evaluates the identity
state. If the provider becomes available,
canRebuildMissingIdentity flips to true, unlocking the self-heal
reindex path.

Refs openclaw#91167

* [AI] fix(memory): allow FTS-only self-heal when chunks are all FTS-only and provider unavailable

When a gateway's memory index identity is 'missing' with chunks already
indexed, canRebuildMissingIdentity stays false if the embedding provider
is unavailable, causing the sync loop to bail out with dirty=true forever.

The previous approach (calling ensureProviderInitialized inside runSync)
was redundant because the public sync() method already initializes the
provider before runSyncWithReadonlyRecovery.

The real fix: when every existing chunk has model='fts-only', rebuilding
the index as FTS-only is safe — no semantic data is lost. So
canRebuildMissingIdentity should also be true when hasOnlyFtsChunks,
even if the provider is unavailable.

Also adds hasSemanticChunks() helper to detect whether any chunks have
a non-fts-only model.

Non-forced test: seeds FTS-only chunks with no meta, syncs without
force, verifies identity transitions from 'missing' to 'valid'.

Refs openclaw#91167

* [AI] fix(memory): gate hasSemanticChunks scan to missing-identity path only

Only compute hasOnlyFtsChunks when identity is missing, chunks exist,
and the provider is unavailable. This avoids scanning the chunks table
for model classification on every ordinary sync.

* test(memory): protect semantic index self-heal

---------

Co-authored-by: Vincent Koc <[email protected]>
xydt-tanshanshan added a commit to xydt-tanshanshan/openclaw that referenced this pull request Jun 15, 2026
CLI openclaw memory index --force writes providerKey based on the
initialized provider's cacheKeyData. When a runtime manager starts
with a different cacheKeyData (e.g. CLI lacked runtime context like
dimensions), the providerKey comparison fails and memory_search is
permanently disabled with 'index provider settings changed'.

When the only identity mismatch is providerKey (provider id + model
match), automatically update meta.providerKey to the current value
instead of blocking search. This mirrors the self-heal pattern
established by openclaw#91897 for missing-identity FTS-only chunks.

Refs openclaw#91902
badgerbees pushed a commit to badgerbees/openclaw that referenced this pull request Jul 8, 2026
…r during sync (openclaw#91897)

* [AI] fix(memory): self-heal missing index identity by initializing provider during sync

When a gateway's memory index identity becomes "missing" with chunks
already indexed, canRebuildMissingIdentity stays false because
this.provider is null (async provider init hasn completed yet), so
needsMissingIdentityReindex is false, and the sync loop bails out
with dirty=true forever — the gateway never self-heals.

Now, when indexIdentity.status is "missing" and a provider is
configured but this.provider is null, runSync() calls
ensureProviderInitialized() first, then re-evaluates the identity
state. If the provider becomes available,
canRebuildMissingIdentity flips to true, unlocking the self-heal
reindex path.

Refs openclaw#91167

* [AI] fix(memory): allow FTS-only self-heal when chunks are all FTS-only and provider unavailable

When a gateway's memory index identity is 'missing' with chunks already
indexed, canRebuildMissingIdentity stays false if the embedding provider
is unavailable, causing the sync loop to bail out with dirty=true forever.

The previous approach (calling ensureProviderInitialized inside runSync)
was redundant because the public sync() method already initializes the
provider before runSyncWithReadonlyRecovery.

The real fix: when every existing chunk has model='fts-only', rebuilding
the index as FTS-only is safe — no semantic data is lost. So
canRebuildMissingIdentity should also be true when hasOnlyFtsChunks,
even if the provider is unavailable.

Also adds hasSemanticChunks() helper to detect whether any chunks have
a non-fts-only model.

Non-forced test: seeds FTS-only chunks with no meta, syncs without
force, verifies identity transitions from 'missing' to 'valid'.

Refs openclaw#91167

* [AI] fix(memory): gate hasSemanticChunks scan to missing-identity path only

Only compute hasOnlyFtsChunks when identity is missing, chunks exist,
and the provider is unavailable. This avoids scanning the chunks table
for model classification on every ordinary sync.

* test(memory): protect semantic index self-heal

---------

Co-authored-by: Vincent Koc <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: memory-core Extension: memory-core P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants