Repository navigation
fix(memory): let the FTS self-heal delete a corrupt index on Windows - #24026
KiroCrewCaptain merged 1 commit into
Conversation
_try_create_db left open the connection that met a corrupt index: the raised error's traceback kept it alive while _get_db's self-heal deleted memory_index.db. Windows refuses to delete a file with an open handle (WinError 32), so there the corrupt index was never replaced and memory search found nothing. Close the connection before the error leaves, as KnowledgeStore and the task store already do before they move a damaged database aside. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UX Review (Opus 5.5, fork) — ⏭️ skippedRevision |
Design Review (Opus 5.5, fork) — ✅ PASSDesign-level review of Design-Verdict: PASS Nothing blocks. The fix closes the connection in the same way the knowledge and task stores already do, and the test that was a strict xfail on Windows now runs in the Windows CI lane. Notes
[DESIGN-REVIEWED] 714ca52 Description read[DESCRIPTION-READ] a09c2f96d6d753d1271c43d1974a8772c58599266334b260bb151c50432e93d9 That sha256 names everything this verdict read: the PR description, plus the 1 evidence file(s) the workflow collected from it and handed this review. It covers that evidence because the media strip replaces every attachment URL with the same placeholder, so an edit swapping one attachment for another would leave the description's captured text identical while what this review judged changed. Recompute it with |
First Principles Review (Opus 5.5, fork) — ✅ PASSPremise-level review of First-Principles-Verdict: PASS Nothing blocks. The change fixes the mechanism behind a defect that a strict xfail on base reproduces. What this change shipsInventory (3 items) — 3 justifiedIntent: on Windows, a corrupt memory search index is rebuilt on its own instead of leaving memory search empty for good. FIX.
[FIRST-PRINCIPLES-REVIEWED] 714ca52 Description read[DESCRIPTION-READ] 18f4ab0a45254d7c792365439ac4fc0e1be8f6c975b1d4d21387f4ef775fd8ef That sha256 names the PR description this verdict read. Recompute it with |
GPT 6.1 Review (fork) — ✅ no blocking findingsReviewed Review detailsNo findings. |
Opus 5.5 Review (fork) — ✅ no blocking findingsReviewed |
|
Re the Design note on |
KiroCrewCaptain
left a comment
There was a problem hiding this comment.
Tier 1 auto-approve: fix (3 files). Criteria: no conflict, no requested changes, security path denylist clean, design-doc gate clean, security checklist all-NO, no reviewer-lane findings, readiness signal fresh for this head SHA, declared category matches the diff. Category: bug fix that closes the index connection when its setup raises, so the self-heal can delete a corrupt memory index on Windows; adds a regression test and drops the test from the Windows expected-failures list.
Problem / Motivation
Goal: on Windows a corrupt memory search index is rebuilt automatically, as it already is on Linux and macOS, instead of leaving memory search empty until someone deletes the file by hand.
When the V1 memory index (
memory_index.db) is corrupt,MemoryStore._get_dbdeletes it and opens a new one, whichrebuild_index()fills from the source files. On Windows the delete fails:The corrupt file stays where it is,
rebuild_index()fails, andsearch()returns nothing. Every later open meets the same file and fails the same way.test_memory_selfheal.py::TestFtsSelfHealGating::test_genuine_corruption_still_self_healsreproduces it (assert 0 >= 1on the search after the rebuild) and is a strict xfail intest/windows-expected-failures.txt.Why it matters
Memory search feeds recall into chat turns. On a Windows install whose index is ever damaged, by a crash or a full disk mid-write, that recall stops for good with only a log warning, although the source files are intact and
memory-skills-hooks.mdsays a corrupt V1 index "can be rebuilt from the source files".Not a goal
What changed (motivation → approach → change)
_try_create_dbconnects and then runs its setup statements. When one of them raisesfile is not a database, the connection is not closed, and the raised error's traceback keeps it open while_get_dbdeletes the file. Linux and macOS delete a file that is still open; Windows does not while a handle is open.src/kiro_crew/memory.py:_try_create_dbcloses the connection when anything after the connect raises, then re-raises. That is the shapeKnowledgeStore.__init__already has, with a comment naming the same Windows reason, and the task store's open.test/windows-expected-failures.txt: thetest_genuine_corruption_still_self_healsline goes, since it now passes on Windows. The two comment lines just above it (# Needs symlink-creation privilege: builds an in-vault FILE symlink ...) were written fortest_md_notebook.py::test_trashing_a_symlink_leaves_its_target_alone, whose entry test(windows): fix the expected-failures matcher and restore link-guard coverage #4467 removed. They stay for now because test(windows): run the stdio probe's stderr stubs as Python scripts #23799 edits the two lines above them, and removing them here makes the two PRs conflict.Backwards compatibility
Compatible: only the failure path changes. A connection whose setup succeeds is returned exactly as before, and the error still reaches
_get_dbunchanged.Removes nothing: the connection this closes is never handed to a caller.
Tests
test/test_memory_selfheal.py:test_self_heal_closes_the_connection_that_found_the_corruption(new): records every connection_get_dbopens on a corrupt index, and checks that each one except the connection it returns is closed (ProgrammingError: Cannot operate on a closed database). Without the fix it fails on every OS. On Windows_get_dbraises the WinError 32 first. On Linux and macOS the stale connection still answersSELECT 1, so the check fails withDID NOT RAISE. That half was not run here, since there is no POSIX host, but on this host an open connection to a corrupt file does answerSELECT 1.test_genuine_corruption_still_self_healsruns on Windows instead of as a strict xfail.Proof: PROVEN test/test_memory_selfheal.py::TestFtsSelfHealGating::test_genuine_corruption_still_self_heals (head 714ca52)
prove.pynames only the test that fails on an assertion. With main'smemory.pyon this Windows host, the new test fails too, on the WinError 32PermissionErrorfrom_get_db.test_memory_selfheal.py11 passed on Python 3.13.5 and on 3.12.13.test_memory*.pyplus every test namingrebuild_index,_get_db,memory_indexor_try_create_db): 2727 passed, 202 skipped, 8 xfailed, with 3 failed and 1 error. All four fail identically on clean main on this host and none touches the index: twotest_auto_research_handlers_coverage_campaign_routescases (cp950UnicodeDecodeError),test_snapshot::TestSecurity::test_symlink_filtered_out(WinError 1314, no symlink privilege) andtest_auto_research_watchdog'sevent_loopfixture error.check_subprocess_encoding,check_black_formatting,check_comment_history,check_brand_name,check_file_line_cap, andtest_flake_pattern_ratchet.py+test_windows_gap_list.py(65 passed) all pass.test/windows-expected-failures.txt(test(windows): burn down six path-separator xfails #23777, test(windows): burn down the gateway-run xfails by patching the rlimit seam #23781, test(windows): pin USERPROFILE in the two subagent cwd tilde tests #23786, test(windows): write the materialize fixtures with LF, skip the symlink case #23788, test(windows): run the stdio probe's stderr stubs as Python scripts #23799, test(crew): give weak crew, agent and subagent tests real checks and merge twins #23887) are clean.Manual verification
N/A — unit coverage sufficient: the tests drive the real
_get_dbself-heal on a real corrupt file, and on Windows that is the reported failure itself.Related Issues
Fixes #24024
Sibling sweep
Swept:
git grep -nE "_is_corruption_error|is_damaged_database_error|quarantine_sqlite_file" -- src/andgit grep -nE '"-wal"' -- src/for code that deletes or moves a damaged SQLite file asideSibling: src/kiro_crew/memory.py:1187 -- changed here
Sibling: src/kiro_crew/knowledge/store.py:379 -- not affected -- closes the failed connection before open_recovering moves the file aside
Sibling: src/kiro_crew/taskq/store.py:548 -- not affected -- closes the connection before _CorruptStore reaches the quarantine
Pattern harvest
Rule candidate: review-prompt
Pattern: a SQLite connection whose setup raises must be closed before the error leaves when a caller deletes or moves the file on that error, because Windows refuses while a handle is open
Checklist
feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert: ...)Contribution License Agreement
🤖 Generated with Claude Code