You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(memory-core): surface swallowed query failures as a degraded result (#12628) (#12629)
* fix(memory-core): surface swallowed Pass-1 query failures as a degraded result (#12628)
The StorageRouter re-ranker's Pass-1 catch returned a clean-empty result on any ChromaDB throw, making a corrupt/unqueryable collection indistinguishable from a genuine no-match -- the silent-failure mode that hid the neo-agent-sessions HNSW corruption for days (parent #12450). Stay non-throwing (Pass-2 resilience preserved), but stamp a _degraded marker (logger.error + corruption-signature tag + collection name), symmetric to the success path's _reRanked. query_summaries / query_raw_memories now surface an explicit QUERY_PATH_DEGRADED envelope so a degraded query path and a genuine no-match stay distinguishable to the caller.
Healthcheck query-canary (AC3) + regression test (AC4) follow in this branch.
Co-Authored-By: Neo Claude Opus <neo-claude-opus@neomjs.com>
* fix(memory-core): degrade healthcheck on a populated-but-unqueryable collection (#12628)
#checkCollections reported count() only, so a collection whose HNSW/vector segment is desynced (count works, query throws 'Error finding id') still passed as healthy -- the same count-only blindspot that hid the neo-agent-sessions corruption (parent #12450). Add a per-collection query-canary mirroring the existing embedding write-canary (TTL-cached, healthy-only): it probes each collection's query path with a dimension-matched vector and degrades status (naming the failing collection + signature) when the re-ranker returns the _degraded marker or the query throws. Wired into both the full and request-fresh-cached healthcheck paths.
Co-Authored-By: Neo Claude Opus <neo-claude-opus@neomjs.com>
* test(memory-core): cover the degraded-query marker contract (#12628)
Pure unit coverage (no Chroma substrate -> runs in CI, unlike the bucket-C-gated QueryReRanker.spec): a Pass-1 throw stamps the _degraded marker (corruption-signature tagged + collection-named) instead of a silent empty, a non-corruption error gets the generic signature, and a genuine empty result stays UNMARKED -- the degraded-vs-no-match distinction the fix exists to preserve. New spec file, so it neither touches nor conflicts with the in-flight QueryReRanker.spec rewrite.
Co-Authored-By: Neo Claude Opus <neo-claude-opus@neomjs.com>
* fix(memory-core): reset the query-canary cache on clearCache + cover the canary degraded path (#12628)
Addresses the failing unit check (gpt RA3): the HealthService cached-healthcheck mock collections had count()/get() but no query(), so the new per-collection query-canary threw and false-degraded the status. Give the shared mock a healthy query() stub (models a reachable collection), and reset #collectionQueryCanaryCache in clearCache() for parity with the embedding write-canary cache + test isolation. Add a canary-degraded regression test: an unqueryable summary collection degrades healthcheck status, names the failing collection, and surfaces the queryCanary block (gpt RA2 consumer coverage, healthcheck half).
Co-Authored-By: Neo Claude Opus <neo-claude-opus@neomjs.com>
* test(memory-core): cover the degraded tool envelopes + document the openapi contract (#12628)
Addresses gpt's RA1 + RA2 (consumer half): query_summaries / query_raw_memories now have CI-runnable degraded-envelope coverage (a degraded collection mocked at the StorageRouter boundary drives querySummaries/queryMemories into a QUERY_PATH_DEGRADED envelope, distinguishable from a genuine no-match). openapi.yaml documents the contract: optional degraded/code/collection/signature/message on QuerySummaries/QueryMemoriesResponse + the healthcheck collections.queryCanary block. OpenApiValidatorCompliance 26/26 (output schemas tolerate the added props; input schemas unchanged).
Co-Authored-By: Neo Claude Opus <neo-claude-opus@neomjs.com>
* refactor(memory-core): move collection query-health probe out of the healthcheck to an on-demand script (#12628)
Operator CR on PR #12629: the per-collection query-canary bloated the always-on healthcheck — response-shape + token + focus-window cost on a poll whose job is a terse "is it healthy?". His call: "a SCRIPT to optionally trigger and inspect => fine."
- Removed the healthcheck query-canary entirely: cache fields, both apply-calls, both private methods, the clearCache reset, and the queryCanary field from the healthcheck openapi schema.
- Moved the probe to StorageRouter.probeCollectionQueryHealth() + ai/scripts/maintenance/probeCollectionQueryHealth.mjs (mirrors GraphService.getLifecycleCensus -> graphLifecycleReport.mjs, the #12602 precedent).
- KEPT the inline _degraded marker + QUERY_PATH_DEGRADED envelope: that is error-path detection (operator: "the concept to detect failures is GOOD"), not always-on bloat.
- Tests: dropped the healthcheck canary test; added a probe-method test proving detection survives the move. StorageRouterDegraded + HealthService 62/62; OpenApiValidatorCompliance 26/26.
Copy file name to clipboardExpand all lines: ai/mcp/server/memory-core/openapi.yaml
+38Lines changed: 38 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -1985,6 +1985,25 @@ components:
1985
1985
type: array
1986
1986
items:
1987
1987
$ref: '#/components/schemas/MemorySearchResult'
1988
+
degraded:
1989
+
type: boolean
1990
+
description: "Present and true ONLY when the query path is degraded (e.g. a corrupt/unqueryable collection whose vector segment throws). Lets callers distinguish a failed query path from a genuine no-match, which omits this field. When true, count is 0 and results is empty but it is NOT a no-match."
1991
+
example: true
1992
+
code:
1993
+
type: string
1994
+
description: "Set to QUERY_PATH_DEGRADED when degraded is true."
1995
+
example: "QUERY_PATH_DEGRADED"
1996
+
collection:
1997
+
type: string
1998
+
description: "The degraded collection (present when degraded)."
1999
+
example: "memory"
2000
+
signature:
2001
+
type: string
2002
+
description: "Corruption/error signature (present when degraded), e.g. chroma-error-finding-id."
2003
+
example: "chroma-error-finding-id"
2004
+
message:
2005
+
type: string
2006
+
description: "Human-readable degraded reason (present when degraded)."
1988
2007
1989
2008
MemorySearchResult:
1990
2009
allOf:
@@ -2144,6 +2163,25 @@ components:
2144
2163
type: array
2145
2164
items:
2146
2165
$ref: '#/components/schemas/SummarySearchResult'
2166
+
degraded:
2167
+
type: boolean
2168
+
description: "Present and true ONLY when the query path is degraded (e.g. a corrupt/unqueryable collection whose vector segment throws). Lets callers distinguish a failed query path from a genuine no-match, which omits this field. When true, count is 0 and results is empty but it is NOT a no-match."
2169
+
example: true
2170
+
code:
2171
+
type: string
2172
+
description: "Set to QUERY_PATH_DEGRADED when degraded is true."
2173
+
example: "QUERY_PATH_DEGRADED"
2174
+
collection:
2175
+
type: string
2176
+
description: "The degraded collection (present when degraded)."
2177
+
example: "summary"
2178
+
signature:
2179
+
type: string
2180
+
description: "Corruption/error signature (present when degraded), e.g. chroma-error-finding-id."
2181
+
example: "chroma-error-finding-id"
2182
+
message:
2183
+
type: string
2184
+
description: "Human-readable degraded reason (present when degraded)."
message : `Memory query path is degraded (${searchResult._degradedSignature}); this is NOT a genuine no-match. Underlying error: ${searchResult._degradedReason}`,
message : `Summary query path is degraded (${searchResult._degradedSignature}); this is NOT a genuine no-match. Underlying error: ${searchResult._degradedReason}`,
0 commit comments