Replace GRDB/SQLite with JSON file persistence#5
Conversation
- addProject now calls gitBackend.status() to get the real current branch - selectWorktree refreshes branch name on switch via refreshWorktreeBranch() - Non-git repos fall back to "main" safely
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aa5cbab3b1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let storeURL = appSupport.appendingPathComponent("mori.json") | ||
| let store = JSONStore(fileURL: storeURL) |
There was a problem hiding this comment.
Migrate existing mori.sqlite data before reading mori.json
Pointing the app at mori.json here breaks every upgrade from the previous SQLite-backed build: JSONStore only reads the new JSON file, so users who already have projects/worktrees/UI state in ~/Library/Application Support/Mori/mori.sqlite will launch into an empty workspace after updating. Because this commit removes all GRDB-based loading code, there is no one-time import or fallback path to preserve existing data.
Useful? React with 👍 / 👎.
| try data.write(to: tmp, options: .atomic) | ||
| _ = try? FileManager.default.removeItem(at: url) | ||
| try FileManager.default.moveItem(at: tmp, to: url) |
There was a problem hiding this comment.
Replace the JSON file without deleting the last good copy first
The write path is not actually atomic here. If removeItem(at: url) succeeds and the subsequent moveItem(at: tmp, to: url) fails, or the process is interrupted between those calls, the previous persisted store is already gone. In that case the next launch hits load's empty-store fallback and the user loses all saved projects/worktrees/UI state from a transient I/O error during save.
Useful? React with 👍 / 👎.
Drop GRDB dependency and replace SQLite with a single JSON file at
~/Library/Application Support/Mori/mori.json.
- Add JSONStore (NSLock thread safety, atomic writes via Data.write)
- Nest worktrees inside projects: {projects: [{project, worktrees[]}]}
- Rewrite repositories to wrap JSONStore (same public API)
- Delete Database.swift, GRDB record types, and Records/
- Link libc++ for GhosttyKit (was transitively provided by GRDB)
- 47 test assertions pass, net -180 lines
e557948 to
befe30d
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: befe30ddbc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ).first!.appendingPathComponent("Mori", isDirectory: true) | ||
| try? FileManager.default.createDirectory(at: appSupport, withIntermediateDirectories: true) | ||
| let storeURL = appSupport.appendingPathComponent("mori.json") |
There was a problem hiding this comment.
Preserve existing mori.sqlite data during upgrade
Upgrading an existing install will now appear to wipe all persisted state: startup always opens ~/Library/Application Support/Mori/mori.json, but the previous builds stored projects/worktrees/UI state in mori.sqlite and there is no fallback or import path anywhere in this change. On first launch after upgrading, loadAll() will see an empty store and users have to re-add every project manually. Please migrate the old SQLite file or keep reading it until a migration has run.
Useful? React with 👍 / 👎.
| try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) | ||
| try? data.write(to: url, options: .atomic) |
There was a problem hiding this comment.
Propagate JSON write failures instead of reporting success
These try?s make every repository save/delete effectively succeed even when the Application Support directory is unwritable or the disk is full. In that case _data is still mutated in memory, so the UI looks correct for the rest of the session, but the change is silently lost on restart. The old GRDB-backed code surfaced write errors through the throwing repository APIs, so this is a regression in persistence guarantees.
Useful? React with 👍 / 👎.
Summary
mori.jsonfileJSONStore(NSLock thread safety, atomic writes viaData.write(.atomic)){projects: [{project, worktrees[]}], uiState}JSONStore— same public API, minimalWorkspaceManagerchangesDatabase.swift, all GRDB record types, andRecords/directorylibc++for GhosttyKit (was transitively provided by GRDB)Why
Mori's data is tiny — a handful of projects/worktrees and one UI state singleton. All operations are simple CRUD with no complex queries, joins, or transactions. A human-readable JSON file is simpler, easier to debug, and has zero external dependencies.
Test plan
mise run devbuilds and launches~/Library/Application Support/Mori/mori.jsonis valid nested JSON