Skip to content

fix(memory-search): stop hybrid fusion from discounting vector-only multimodal results#92196

Merged
steipete merged 1 commit into
openclaw:mainfrom
yetval:fix/memory-hybrid-multimodal-rank
Jul 21, 2026
Merged

fix(memory-search): stop hybrid fusion from discounting vector-only multimodal results#92196
steipete merged 1 commit into
openclaw:mainfrom
yetval:fix/memory-hybrid-multimodal-rank

Conversation

@yetval

@yetval yetval commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Hybrid memory search ranks results with finalScore = vectorWeight * vectorScore + textWeight * textScore. mergeHybridResults (extensions/memory-core/src/memory/hybrid.ts) applied that formula uniformly to every candidate. Image and audio chunks only carry a synthetic label as text, e.g. Image file: media/IMG_0007.png (buildMemoryMultimodalLabel, packages/memory-host-sdk/src/host/multimodal.ts). A natural-language query shares no words with that label, so a media chunk's BM25/text score is structurally near zero and it is seeded into the merge with textScore: 0. Under the default 0.7 vector / 0.3 text weights, a semantically strong media match is scaled to 70% of its vector score and ranks below a weaker text chunk that matches query keywords.

The fix makes fusion modality-aware. mergeHybridResults keeps the established weighted formula for every candidate, and only for a vector-only classified media chunk does it collapse the score to the vector signal:

-    const score = params.vectorWeight * entry.vectorScore + params.textWeight * entry.textScore;
+    const dropMediaTextSignal =
+      entry.hasVector &&
+      !entry.hasKeyword &&
+      params.vectorWeight > 0 &&
+      params.isNonTextMediaPath?.(entry.path) === true;
+    const contentScore = dropMediaTextSignal
+      ? entry.vectorScore
+      : params.vectorWeight * entry.vectorScore + params.textWeight * keywordScore;

For a non-text media chunk that has only a vector signal, dropping the near-zero text weight and renormalizing by the remaining vector weight collapses the score to its vector score, on the same [0,1] scale as text candidates. Every other candidate keeps the exact established weighted formula, so scoring scale, minScore filtering, and result ordering outside the vector-only media case are unchanged, including for custom weights that do not sum to one.

The manager supplies the predicate from the already-known file path, reusing the classifier the sync path uses (extensions/memory-core/src/memory/manager.ts):

isNonTextMediaPath: (path) =>
  classifyMemoryMultimodalPath(path, this.settings.multimodal) !== null,

classifyMemoryMultimodalPath returns null when multimodal search is disabled, so the predicate is false for every path and behavior is identical to today on text-only setups. Modality is derived, not persisted, so there is no schema change or migration.

Keyword-only media is preserved (review follow-up)

The first revision dropped the text weight for any media path. That zeroed a media chunk matched only by keyword (its label tokens) and no vector signal: (0.7*0 + 0*text)/0.7 = 0, dropping it from results. The merge now tracks an explicit hasVector flag per candidate and only drops the synthetic-label text signal when the candidate also has a vector signal. A keyword-only media hit keeps its normal text-weighted score, matching pre-fix behavior. Covered by a new regression test.

Zero vector-weight configuration preserved (re-review follow-up)

vectorWeight may legitimately be 0 (existing memory tests run { vectorWeight: 0, textWeight: 1 }). If a classified media chunk appeared in both the vector and keyword result sets, hasVector made the previous revision drop textWeight; weightSum then became 0 + 0 and the candidate scored 0, suppressing a valid keyword match. The drop is also gated on a positive configured vector weight, so when vectorWeight is 0 the synthetic text signal is never the only meaningful signal and is kept. Covered by a regression test for a media candidate present in both result sets under vectorWeight 0, textWeight 1.

Both-signal media keeps its real keyword score (re-review follow-up)

Media labels are indexed and searchable (a manager test expects search("image")/search("audio") to find media). So a user who searches the label or filename can get the same media chunk back from FTS and from vector search at once. Gating the drop only on hasVector discarded the BM25/text score for that both-signal case, ignoring a real keyword match under positive vectorWeight and down-ranking a media file that legitimately matched FTS. The drop is now also gated on the candidate having no keyword signal, so the synthetic-label text weight is dropped only for genuinely vector-only media:

     const dropMediaTextSignal =
       entry.hasVector &&
+      !entry.hasKeyword &&
       params.vectorWeight > 0 &&
       params.isNonTextMediaPath?.(entry.path) === true;

hasKeyword is tracked the same way as hasVector: it is set when a candidate is present in the keyword (FTS) result set. A vector-only media chunk (the #44540 case) still drops the synthetic label and scores on its vector signal; a media chunk with a real FTS hit now keeps the keyword contribution. Covered by a new regression test for a media candidate present in both result sets under positive vector weight.

Normalization limited to vector-only classified media (re-review follow-up)

An earlier revision computed contentScore = weighted / weightSum for every candidate. weightSum equals 1 only when the configured weights already sum to one; the public config exposes vectorWeight and textWeight independently, and existing tests run { vectorWeight: 1, textWeight: 1 }. Under such a configuration the division halved the score of ordinary text and vector-only non-media candidates, which could change ordering and minScore filtering outside the reported bug. The renormalization is now applied only when dropMediaTextSignal is true; every other candidate retains the established unnormalized weighted formula exactly:

-    const effectiveTextWeight = dropMediaTextSignal ? 0 : params.textWeight;
-    const weightSum = params.vectorWeight + effectiveTextWeight;
-    const weighted =
-      params.vectorWeight * entry.vectorScore + effectiveTextWeight * keywordScore;
-    const contentScore = weightSum > 0 ? weighted / weightSum : 0;
+    const contentScore = dropMediaTextSignal
+      ? entry.vectorScore
+      : params.vectorWeight * entry.vectorScore + params.textWeight * keywordScore;

Because dropMediaTextSignal is gated on params.vectorWeight > 0, collapsing the media score to entry.vectorScore is exactly (vectorWeight * vectorScore) / vectorWeight with no division by zero. Covered by two new regression tests, using vectorWeight 1, textWeight 1 (weights that do not sum to one): a vector-only non-media candidate and a both-signal non-media candidate each keep the established weighted score.

What is intentionally out of scope: persisting a modality column on chunks, and any change to the text-only ranking path.

Rebase onto current main and the TypeScript LOC ratchet

This branch was rebased onto current main. The changed-file TypeScript LOC ratchet on main forbids an oversized legacy file from growing, and extensions/memory-core/src/memory/manager.ts is already 1671 lines, so plumbing the media predicate into the merge call tripped the gate. The merge wrapper's inline parameter type moved to hybrid.ts, which owns the merge contract, and is referenced as ManagerHybridMergeParams. manager.ts now shrinks to 1660 lines. The modality-aware fusion fix is unchanged.

Real behavior proof

Behavior addressed: hybrid fusion discounted a vector-only image match by the configured text weight, so a semantically strong media result ranked below a weaker keyword-matching text note.
Real environment tested: the real production memory retrieval path driven on pristine main (1708faf85b) and on PR head c02d11dc7f with identical inputs: a real node:sqlite memory index built with the production ensureMemoryIndexSchema, a real FTS5 index scored by the production searchKeyword with real BM25 ranking, the real classifyMemoryMultimodalPath modality classifier wired exactly as MemoryIndexManager wires it, and the real mergeHybridResults fusion under the default vectorWeight 0.7, textWeight 0.3. The corpus is one image file, one keyword-matching note, and 18 filler documents so BM25 has real IDF.
Exact steps or command run after this patch: indexed the corpus, ran the query underwater coral reef survey through the production keyword search and hybrid fusion, then reverted only hybrid.ts to its pristine main contents and re-ran the identical scenario.
Evidence after fix:

# BEFORE (pristine main 1708faf85b)
query: underwater coral reef survey
fusion weights: vectorWeight=0.7 textWeight=0.3
classifyMemoryMultimodalPath("memory/media/IMG_0007.png") -> image
classifyMemoryMultimodalPath("memory/field-notes.md") -> null
ranked memory search results:
  #1  score=0.9376  vector=0.9487  bm25=0.9116  memory/field-notes.md
  #2  score=0.7000  vector=1.0000  bm25=0.0000  memory/media/IMG_0007.png
top result: memory/field-notes.md

# AFTER (this patch, identical inputs, head c02d11dc7f)
query: underwater coral reef survey
fusion weights: vectorWeight=0.7 textWeight=0.3
classifyMemoryMultimodalPath("memory/media/IMG_0007.png") -> image
classifyMemoryMultimodalPath("memory/field-notes.md") -> null
ranked memory search results:
  #1  score=1.0000  vector=1.0000  bm25=0.0000  memory/media/IMG_0007.png
  #2  score=0.9376  vector=0.9487  bm25=0.9116  memory/field-notes.md
top result: memory/media/IMG_0007.png

Observed result after fix: the image chunk carries a strong vector signal (1.0000) but a structurally zero BM25 signal, because its only indexed text is the synthetic Image file: label. Before the patch the 0.3 text weight scaled it down to 0.7000 and it lost to the weaker note at 0.9376. After the patch it scores on its vector signal (1.0000) and surfaces first, while the note keeps its identical 0.9376 score, so the ranking flips to put the strongest semantic match on top.
What was not tested: no live embedding provider request was made, so the query and chunk vectors are deterministic local values rather than Gemini-served embeddings; the approximate-nearest-neighbour vector index lookup was not exercised, and the vector scores were supplied directly to fusion. Everything else on the retrieval path (schema, FTS5, BM25 ranking, modality classification, fusion) is the real production code. A full production build was not run.

Verification

  • Real before/after through the production memory retrieval path above (real node:sqlite schema, FTS5/BM25 keyword search, real modality classifier, real fusion), pristine main vs this patch on identical inputs.
  • Regression coverage in extensions/memory-core/src/memory/hybrid.test.ts: vector-only media scored on its vector signal without the text-weight discount; text-candidate scoring unchanged when a media predicate is supplied; keyword-only media preserved (not dropped to zero); a classified media candidate present in both result sets under vectorWeight 0, textWeight 1 keeps its keyword score; a classified media candidate present in both result sets under positive vector weight keeps its keyword score instead of being discounted; and, under vectorWeight 1, textWeight 1, a vector-only non-media candidate and a both-signal non-media candidate each keep the established weighted score.
  • node scripts/test-projects.mjs extensions/memory-core/src/memory/hybrid.test.ts (22 passed).
  • node scripts/test-projects.mjs extensions/memory-core/src/memory/manager-search.test.ts (34 passed).

Closes #44540

@yetval
yetval marked this pull request as ready for review June 11, 2026 14:58
@openclaw-barnacle openclaw-barnacle Bot added the proof: supplied External PR includes structured after-fix real behavior proof. label Jun 11, 2026
@clawsweeper

clawsweeper Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 13, 2026, 1:03 PM ET / 17:03 UTC.

Summary
The PR makes hybrid memory fusion score vector-only classified image/audio results on their vector signal while preserving the established weighted formula for all other candidates.

PR surface: Source +22, Tests +235. Total +257 across 3 files.

Reproducibility: yes. at high confidence from source and the supplied production-path before/after: the unchanged weighted formula discounts a vector-only media score by vectorWeight. The review did not independently run the latest main SHA with a live embedding provider, so the structured status remains source-reproducible rather than independently reproduced.

Review metrics: 3 noteworthy metrics.

  • Scoring exceptions: 1 targeted exception added. Only vector-only classified non-text media departs from the established weighted formula.
  • Regression scenarios: 7 edge cases covered. The tests protect keyword-only, both-signal, zero-weight, non-media, and custom-weight behavior around the new branch.
  • Persistent surfaces: 0 config or schema changes. Modality is derived with the existing classifier, so upgrades require no migration or new operator setting.

Stored data model
Persistent data-model change detected: unknown-data-model-change: extensions/memory-core/src/memory/hybrid.test.ts, unknown-data-model-change: extensions/memory-core/src/memory/hybrid.ts, unknown-data-model-change: extensions/memory-core/src/memory/manager.ts, vector/embedding metadata: extensions/memory-core/src/memory/hybrid.test.ts, vector/embedding metadata: extensions/memory-core/src/memory/hybrid.ts, vector/embedding metadata: extensions/memory-core/src/memory/manager.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #44540
Summary: This PR is the focused candidate fix for the canonical multimodal hybrid-ranking issue; the other memory items concern separate capability or keyword-only filtering behavior.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦞 diamond lobster
Proof: 🦞 diamond lobster
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

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

Risk before merge

  • [P1] Merging intentionally changes score scale, ordering, and minScore eligibility for vector-only classified media in existing multimodal indexes; memory-core owners should accept that bounded compatibility change.
  • [P1] The proof directly exercises fusion with deterministic vector scores rather than a live embedding-provider and approximate-nearest-neighbor lookup, although those omitted components do not implement the changed scoring branch.

Maintainer options:

  1. Land the bounded scoring fix (recommended)
    Accept the intentional ranking change for vector-only classified media after normal memory-core owner review and exact-head landing checks.
  2. Request full provider-path proof
    Pause landing if owners require a live Gemini embedding and vector-index retrieval run in addition to the already sufficient fusion-path proof.

Next step before merge

  • No automated repair remains; the current head is ready for normal memory-core owner review and landing gates.

Security
Cleared: The diff changes local memory scoring and tests only, with no dependency, workflow, secret, permission, download, publishing, schema, or supply-chain surface.

Review details

Best possible solution:

Land the narrow classifier-backed fusion exception after memory-core owner review, retain the regression matrix for custom weights and mixed signals, and close #44540 once merged.

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

Yes at high confidence from source and the supplied production-path before/after: the unchanged weighted formula discounts a vector-only media score by vectorWeight. The review did not independently run the latest main SHA with a live embedding provider, so the structured status remains source-reproducible rather than independently reproduced.

Is this the best way to solve the issue?

Yes. Applying a narrowly gated exception at the shared fusion boundary, using the existing multimodal path classifier and preserving every sibling scoring case, is more maintainable than persisting modality or changing global weight semantics.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 70feb13ce7f5.

Label changes

Label justifications:

  • P2: This is a bounded memory-retrieval ranking bug affecting multimodal results without data loss, outage, or core-runtime unavailability.
  • merge-risk: 🚨 compatibility: The PR intentionally changes score scale, result ordering, and threshold eligibility for existing vector-only media searches.
  • merge-risk: 🚨 session-state: Changing memory-search ranking can alter which stored image or audio context is selected for an agent run.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR supplies convincing after-fix live output through the production SQLite, FTS/BM25, classifier, and fusion path, with an identical-input baseline and explicit limitations appropriate to the changed fusion logic.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR supplies convincing after-fix live output through the production SQLite, FTS/BM25, classifier, and fusion path, with an identical-input baseline and explicit limitations appropriate to the changed fusion logic.
Evidence reviewed

PR surface:

Source +22, Tests +235. Total +257 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 2 39 17 +22
Tests 1 235 0 +235
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 274 17 +257

What I checked:

Likely related people:

  • lsdcc01: Credited on the merged history that introduced BM25 relevance scoring and the hybrid score path now being adjusted. (role: introduced hybrid scoring behavior; confidence: medium; commits: e45d62ba26; files: extensions/memory-core/src/memory/hybrid.ts)
  • steipete: Committed the merged hybrid BM25 relevance work and is a likely routing candidate for the intended scoring semantics. (role: merger and adjacent area contributor; confidence: medium; commits: e45d62ba26; files: extensions/memory-core/src/memory/hybrid.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.
Review history (4 earlier review cycles)
  • reviewed 2026-06-21T17:38:06.872Z sha a9d17f0 :: needs maintainer review before merge. :: none
  • reviewed 2026-06-30T22:50:09.196Z sha a9d17f0 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-12T00:51:00.640Z sha fa443f4 :: needs changes before merge. :: [P1] Preserve scores when configured weights do not sum to one
  • reviewed 2026-07-12T01:55:34.509Z sha 58b2466 :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added the rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. label Jun 11, 2026
@yetval

yetval commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 11, 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: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 11, 2026
@yetval

yetval commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both review follow-ups in ba87eaf5342 and refreshed the proof.

Preserve keyword-only classified media candidates — the first revision dropped the text weight for every media path, which zeroed a media chunk matched only by keyword (its label tokens) with no vector signal: (0.7*0 + 0*text)/0.7 = 0. The merge now tracks an explicit hasVector flag and drops the synthetic-label text signal only when the candidate also has a vector signal, so a keyword-only media hit keeps its normal text-weighted score. New regression test: preserves keyword-only media candidates instead of dropping them to zero.

Real indexed multimodal search proof — added a before/after through a real MemoryIndexManager running the full pipeline (SQLite, FTS5/BM25 with a 20-doc corpus for real IDF, vector store, modality classification, hybrid fusion). Query underwater coral reef survey; the query embedding aligns with the indexed image vector while sharing no words with its Image file: label (the reported semantic-only-media case):

BEFORE (origin/main):  top = field-notes.md (0.9396); image discounted to 0.7000 (vec 1.0, text 0), buried
AFTER  (patch):        top = IMG_0007.png (1.0000); field-notes.md #2 (0.9396)

Run via vitest with isolated module caches per side. Embeddings are a deterministic local stub (no Gemini key in this environment); every indexing/FTS/vector/fusion path is the real code. Full output and limitations are in the PR body.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 11, 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: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 11, 2026
@yetval

yetval commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

This workspace is deactivated. Select an active workspace and try again.
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@yetval

yetval commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-run

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 12, 2026
@yetval
yetval force-pushed the fix/memory-hybrid-multimodal-rank branch from ba87eaf to 3220af9 Compare June 12, 2026 03:07
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 12, 2026
@yetval

yetval commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the [P1] zero-vector-weight finding in 3220af9.

dropMediaTextSignal now also requires a positive configured vectorWeight, so the synthetic media text signal is dropped only when the vector signal it defers to actually carries weight. Under vectorWeight: 0, textWeight: 1, a classified media chunk present in both the vector and keyword result sets keeps its keyword score instead of collapsing to weightSum = 0 -> score = 0. The default-weight path is unchanged.

Real before/after through the production mergeHybridResults fusion (with the real classifyMemoryMultimodalPath predicate), comparing the previously reviewed head ba87eaf vs this patch:

BEFORE (ba87eaf):  vectorWeight=0 textWeight=1  score=0.0000  surfaced=false  -> SUPPRESSED
AFTER  (patch):    vectorWeight=0 textWeight=1  score=0.8000  surfaced=true   -> PRESERVED
default-weight (0.7/0.3): vector-only image score=1.0000 in both -> unchanged

New regression test covers a media candidate present in both result sets under vectorWeight 0, textWeight 1. Full output and the unchanged #44540 before/after are in the PR body.

@clawsweeper re-review

@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 16, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 20, 2026
@yetval
yetval force-pushed the fix/memory-hybrid-multimodal-rank branch from a9d17f0 to fa443f4 Compare July 12, 2026 00:37
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 12, 2026
@yetval

yetval commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed the [P2] rank-up move (and the paired [P1] finding at hybrid.ts:185): normalization is now limited to vector-only classified media. contentScore keeps the established unnormalized weighted formula for every candidate; only when dropMediaTextSignal is true does it collapse to entry.vectorScore. Non-media and text candidates are no longer divided by weightSum, so custom weights that do not sum to one preserve their score scale, minScore filtering, and ordering.

Added the requested coverage under vectorWeight 1, textWeight 1: a vector-only non-media candidate and a both-signal non-media candidate each keep the established weighted score. Both fail against the previously reviewed head (0.3 and 0.75, i.e. halved) and pass on this patch (0.6 and 1.5). Real before/after through the production mergeHybridResults is in the PR body. Head 58b2466dae; hybrid.test.ts 22 passed, manager-search.test.ts 34 passed, oxlint clean.

@clawsweeper

clawsweeper Bot commented Jul 12, 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: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jul 12, 2026
@yetval
yetval force-pushed the fix/memory-hybrid-multimodal-rank branch 2 times, most recently from 20fed0e to 5cce9c6 Compare July 13, 2026 16:10
@steipete steipete self-assigned this Jul 21, 2026
@steipete
steipete force-pushed the fix/memory-hybrid-multimodal-rank branch from c02d11d to 156d46a Compare July 21, 2026 08:25
@clawsweeper

clawsweeper Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: fix(memory-search): stop hybrid fusion from discounting vector-only multimodal results This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@steipete
steipete merged commit e942d7e into openclaw:main Jul 21, 2026
92 of 94 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

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 merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hybrid search BM25 component penalizes multimodal (image/audio) results

2 participants