[Review] migrateSensitiveFieldsToVault is not actually idempotent — duplicate-write on crash, data-loss on partial run #175
Labels
No labels
blocked:depends-p1
blocked:gate-research
blocked:lint-failed
blocked:needs-decision
blocked:needs-human
blocked:verify-failed
bug
data-loss
documentation
enhancement
feature-request
in-progress
needs-decompose
needs-triage
pwa
quickfix
security
security/high
security/medium
test
test-coverage
triage-approved
vault
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/kimi_agent_kin_app_sync#175
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Finding
VaultStore.migrateSensitiveFieldsToVault(src/vault/VaultStore.ts:503-575) ships with a test at src/vault/tests/vaultMigration.test.ts:95-129 claiming "is idempotent across repeated runs." The test passes only because the first run strips the source data; the migration itself has no dedup logic.Repro
The window between (1) and (4) is unbounded. If the process dies between those awaits (refresh, OOM, OS kill), the second run sees the main row still has
mood, copies it again, and writes a secondvaultMoodrow with the sameinteractionId. There is novaultMood.where({interactionId}).first()check beforeadd().Failure modes
interactionIdon any crash mid-loop or on cold-boot re-run before the loop completes.for awaitover Dexie writes on the main thread, with no progress UI and no "are we done?" flag.Suggested fix shape
vaultMood.where({interactionId}).first()check beforeadd()(and same for intimacy/conflicts/quality).migrationMarkerrow invaultIntimateSettingsor a dedicatedvaultMigrationStatetable; only migrate rows for persons where the marker is absent.add, assert second run produces no duplicate).Severity
High. This is the privacy-boundary migration. Data duplication or loss here directly affects the vault's correctness guarantee.
Discovered by
Manual code review pass over the last ~10 merged commits (intimate-state feature #168/#169 + recent hardening), 2026-09-14.
Fixed in commit
a32d000(fix(#175): make migrateSensitiveFieldsToVault crash-safe), now onorigin/main.The kimi_agent_kin_app_sync runner originally landed the fix locally but its
git pushwas rejected becauseorigin/mainhad advanced between commit and push (other kilo-gsd runs had landed in the same window). Watcher rebased onto the neworigin/main, re-pushed both #175 and the unrelated #179 commit.Three hardening changes per the commit message:
vaultMood/vaultIntimacy/vaultConflicts/vaultQualitywrite, look up the existing row byinteractionId. Skip the add if it already exists. Protects against duplicate rows after a crash between vault-write and main-strip.vaultMigrationStatemarker — once all four sensitive tables for apersonIdare processed, the marker records it. Persons with a marker are skipped entirely on subsequent runs. Crash mid-person leaves the marker absent, so the next run reprocesses (and the dedup checks above prevent duplicates).Dexie.update()on the same value is a no-op).Two regression tests in
src/vault/__tests__/vaultMigration.test.ts:Verified locally:
npx vitest run src/vault/__tests__/vaultMigration.test.ts→ 5/5 PASS (934ms).Closing as already-fixed.
Commit
a32d000(fix(#175): make migrateSensitiveFieldsToVault crash-safe) landed onmainat 2026-09-14 (today, before this triage session reviewed the file).The chat-agent code review read against a stale HEAD and reported this issue as open. The fix was already in place when the review started — the agent failed to re-check HEAD between reads.
No work to do.