Skip to content

fix: store history in an app-namespaced SwiftData store - #36

Merged
iuhoay merged 1 commit into
mainfrom
fix/history-store-namespaced-store
Jun 15, 2026
Merged

fix: store history in an app-namespaced SwiftData store#36
iuhoay merged 1 commit into
mainfrom
fix/history-store-namespaced-store

Conversation

@iuhoay

@iuhoay iuhoay commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Problem

Translation history could silently disappear. HistoryStore created its SwiftData database with a bare ModelContainer(for: HistoryItem.self) and no URL. For a non-sandboxed app that resolves to the shared ~/Library/Application Support/default.store — a path every other non-sandboxed app using a bare ModelContainer also writes to. When another app opens that file with its own schema, Core Data recreates the store and drops our HistoryItem table, wiping the user's whole history. (Observed in the wild: the shared file ended up holding another app's APIRequestModel entity, with no HistoryItem table left anywhere on disk.)

Fix

  • Pin the store to Application Support/<bundle-id>/Lumo.store. A bundle-id-scoped path can't collide with another app, and also keeps the Debug "Lumo Dev" history separate from Release.
  • On first launch with the new path, importLegacyHistory(from:into:) does a one-time, non-destructive rescue of any history still in the old shared store:
    • A read-only Core Data metadata probe first confirms the file actually holds our HistoryItem entity. If it holds another app's schema, nothing is touched.
    • The legacy store is opened allowsSave: false and never deleted.
    • Every step is best-effort — a failure just means an empty list, never a crash.

Tests

New LegacyStoreRescueTests suite (3 tests):

  • imports rows from a matching legacy store (and leaves the file in place)
  • skips a foreign-schema store — the safety property
  • no-ops cleanly when the legacy file is missing

Full suite: Test run with 100 tests in 15 suites passed.

Note

History already lost before this fix is unrecoverable (the shared store was overwritten). The rescue only helps installs where the old history still survives. API-key storage is unchanged.

🤖 Generated with Claude Code

A non-sandboxed app's bare ModelContainer(for:) resolves to the shared
~/Library/Application Support/default.store. Any other non-sandboxed app
using the same default writes to that file too; when one opens it with a
different schema, Core Data recreates the store and silently drops our
HistoryItem table — the user's whole history vanishes.

Pin the store to Application Support/<bundle-id>/Lumo.store so it can't
collide with another app (and keeps Debug "Lumo Dev" history separate from
Release). On first launch with the new path, importLegacyHistory(from:into:)
does a one-time, non-destructive rescue of any history still in the old
shared store: it confirms via read-only Core Data metadata that the file
holds our HistoryItem entity before opening it read-only, so another app's
data is never touched.

Add LegacyStoreRescueTests covering the matching-store import, the
foreign-schema skip, and the missing-file no-op.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@iuhoay
iuhoay merged commit 80a123a into main Jun 15, 2026
1 check passed
@iuhoay
iuhoay deleted the fix/history-store-namespaced-store branch June 15, 2026 15:03
@iuhoay iuhoay mentioned this pull request Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant