v3.2.10 — /recall 결함 sweep (NEXT-36)
Summary
/recall 스킬 (memory_search.py + search.py + recall_cli.py) 에 대한 systematic-debugging 4-phase + codex 독립 검증 sweep. 2 fix + 4 false alarm confirm + 7 회귀 가드 + 1 cosmetic 정정. 2 연속 라운드 0 critical/medium 으로 수렴.
Critical Fix
memory_search._vec_top_k — mat ↔ meta 인덱스 미스매치
- 증상:
memories_vec에 invalid embedding row (빈 bytes, dim mismatch) 1건만 있어도:results = [(meta[i][0], ...) for ..., i in enumerate(idx_sorted)]→IndexError또는 잘못된 path 반환raw_map의 cosine 값을 잘못된 path 에 매핑 (cross-contamination, p2 cosine 0.775 → 0.0 게이트 차단, p3 cosine 0.884 → 0.775)
- root cause:
mat[i] = arr(row 인덱스) 와meta.append(...)(valid 만) 의 인덱스 정합 깨짐. 같은 모듈의search.vec_candidates는valid카운터 +mat[:valid]안전 패턴이었으나 한 함수만 fix 누락 - fix:
valid카운터 +mat[:valid]패턴으로 교체. 두 모듈 안전 패턴 통일 - silent 이유: 운영 DB invalid row 0건. 인덱서가 partial write 1회만 일으키면
recall_memory의 outerexcept Exception가드가 잡아 빈 결과 반환 → 사용자는 검색 0건으로만 보고 결함 모름
Medium Fix
_resolve_wikilink — ORDER BY path 추가
- 증상: 다중 후보 시 SQLite 반환 순서 미보장 → 같은 wikilink 가 호출마다 다른 메모리로 resolve
- fix:
path LIKE ?쿼리에ORDER BY path추가 (lexicographic 안정 결정성)
False Alarm 분류 (의도된 동작 confirm)
| 항목 | 분류 |
|---|---|
| M1: 무의미 query ("1234") 5건 통과 | /recall 명시 호출의 user-driven sift 정책 (score_threshold=0.0, raw_cosine_min=0.32) 일치 |
M3: gemma_rerank fallback 정렬 |
RRF rank 기준 첫 K 가 합리적, 게이트 후 절대-상대 분리 정책 |
L1: fts_escape 두 모듈 중복 |
8/8 sample 동등성 confirm. DRY 통합 비용 > 효용. skew 회귀 가드만 추가 |
L2: recall_cli.py top_k 옵션 없음 |
spec(recall.md) 의도, 명시 호출은 user-driven sift |
회귀 가드 7건 (tests/test_memory_search.py)
TestVecTopKInvalidRowRegression× 4 (empty / bad-dim / all-invalid / all-valid backwards compat)TestFtsEscapeParity× 1 (search.fts_escapevsmemory_search._fts_escape12 sample 동등성)TestResolveWikilinkDeterminism× 2 (lex order + 20회 반복 안정성)
검증
- pytest 422 → 429 passed (회귀 0)
- 운영 본
~/.claude/scripts/mindvault/memory_search.pymd5 동기화 - 실 query verify — 한국어/영문 query + sessions search + Gemma summary 모두 정상
- Round 2 자체 sweep → 0 critical/medium
- codex:codex-rescue 독립 검증 → 0 critical/medium (1 cosmetic 주석 정정만)
- Round 3 자체 sweep (alias race / raw_cosine_map 사용 / import 순환) → 0 new
- → 2 연속 라운드 0건 close 수렴 기준 (
feedback-systematic-debugging-code-review) 충족
Files changed
src/memory_search.py(+19/-4)tests/test_memory_search.py(+170)
누적
결함 99건.