diff --git a/agents/review-state.md b/agents/review-state.md index e6bebe31..52c8dea9 100644 --- a/agents/review-state.md +++ b/agents/review-state.md @@ -8,7 +8,7 @@ Review state (marks and notes) is persistent across app restarts, local to the u - Canonical implementation: `jayjay_review::ReviewStore` (marks in `marks.rs`, notes in `note_store.rs`, reconciliation in `reconcile.rs`). - Persistence and file format: [Storage Guide](storage.md). -- SwiftUI: `Shared/ReviewStore.swift` is an `@Observable` UniFFI facade with a per-file marks cache and a one-time UserDefaults legacy import. +- SwiftUI: `Shared/ReviewStore.swift` is an `@Observable` UniFFI facade with a per-file marks cache. - GPUI: one process-global store; mutate only through `window/review.rs::mutate` (refresh from disk first). Render-path reads use `refresh_if_stale`. Note reconciliation loads asynchronously on the view model (`loaders/review_notes.rs`, generation-guarded). ## Marks diff --git a/agents/storage.md b/agents/storage.md index 2ba25661..f80ca168 100644 --- a/agents/storage.md +++ b/agents/storage.md @@ -18,7 +18,7 @@ Rust stores resolve platform-native directories through `directories::ProjectDir | Pinned repositories | `jayjay-core`; SwiftUI via UniFFI; GPUI directly | `repositories.json` in the shared config directory | An ordered `repositories` array of canonical absolute UTF-8 repository paths. New pins are inserted first; empty paths and exact duplicates are removed on load. | | Review marks and notes | `jayjay-review`; SwiftUI via UniFFI; GPUI and CLI directly | `review_store.json` in the shared config directory | File/hunk review marks keyed by `change_id|path`, content identities, and local review notes including path, side, line, anchor context, body, timestamps, and resolution state. | | SwiftUI settings and history | SwiftUI-only `AppSettings` | `UserDefaults` for bundle `dev.hewig.jayjay` | Appearance and font, diff options, layout, confirmations, onboarding, editor/terminal choices, update channel, sponsorship state, up to 12 recent repositories, and the last opened repository. | -| SwiftUI auxiliary state | SwiftUI components | The same `UserDefaults` domain | Command-palette position. A legacy `jayjay.reviewedFiles` blob is imported once into the shared review store and then removed. | +| SwiftUI auxiliary state | SwiftUI components | The same `UserDefaults` domain | Command-palette position. | | GPUI settings and history | GPUI-only Rust `AppConfig` | `config.toml` in the platform config directory | Appearance and font, diff options, layout, tools, feature confirmations, onboarding, update channel, window bounds/maximized state, and up to 12 recent repositories. | Recent repositories are history, not projects. Each shell owns its own recent list. Pins are persistent projects and are intentionally shared by both shells. diff --git a/shell/mac/Sources/JayJay/Shared/ReviewStore.swift b/shell/mac/Sources/JayJay/Shared/ReviewStore.swift index 9c179a80..5d78b9a2 100644 --- a/shell/mac/Sources/JayJay/Shared/ReviewStore.swift +++ b/shell/mac/Sources/JayJay/Shared/ReviewStore.swift @@ -6,8 +6,6 @@ import JayJayCore final class ReviewStore { typealias ReviewNote = NoteEntry - private static let legacyStorageKey = "jayjay.reviewedFiles" - let storeURL: URL? var notes: [ReviewNote] // Observable stand-in for the cache's contents: SwiftUI views read marks during render (gutter stripes, file rows), and without a tracked read a toggle would not re-render them until something else invalidated the view. @@ -17,7 +15,6 @@ final class ReviewStore { init() { storeURL = reviewStorePath().map { URL(fileURLWithPath: $0) } notes = [] - importLegacyMarks(from: .standard) } /// Test seam: persist to an explicit file instead of the shared store path. @@ -149,30 +146,4 @@ final class ReviewStore { marksCache.removeAll() marksVersion &+= 1 } - - // MARK: Legacy migration - - /// One-time import of marks the old UserDefaults-backed store left behind; runs only while no shared store file exists yet, then drops the legacy blob. - func importLegacyMarks(from defaults: UserDefaults) { - guard let storeURL, !FileManager.default.fileExists(atPath: storeURL.path), - let data = defaults.data(forKey: Self.legacyStorageKey), - let raw = try? JSONSerialization.jsonObject(with: data) as? [String: Any] - else { return } - for (key, value) in raw { - guard let separator = key.firstIndex(of: "|"), - let dict = value as? [String: Any], - let identity = dict["identity"] as? String, - !identity.isEmpty - else { continue } - let changeId = String(key[..= 0 ? UInt32($0) : nil } ?? [] - if dict["file_marked"] as? Bool ?? false { - markReviewed(changeId: changeId, path: path, identity: identity) - } else if !hunks.isEmpty { - setReviewedHunks(changeId: changeId, path: path, identity: identity, hunkIndices: hunks) - } - } - defaults.removeObject(forKey: Self.legacyStorageKey) - } } diff --git a/shell/mac/Tests/JayJayTests/ReviewStoreTests.swift b/shell/mac/Tests/JayJayTests/ReviewStoreTests.swift index 82cdfedb..9becd08d 100644 --- a/shell/mac/Tests/JayJayTests/ReviewStoreTests.swift +++ b/shell/mac/Tests/JayJayTests/ReviewStoreTests.swift @@ -171,30 +171,6 @@ final class ReviewStoreTests: XCTestCase { ) } - func testLegacyDefaultsImportOnFirstRun() throws { - let url = tempStoreURL() - let suiteName = "review-migration-\(UUID().uuidString)" - let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName)) - defer { defaults.removePersistentDomain(forName: suiteName) } - let legacy: [String: Any] = [ - "c1|a.txt": ["identity": "idA", "file_marked": true], - "c1|b.txt": ["identity": "idB", "file_marked": false, "hunks": [1]] - ] - try defaults.set(JSONSerialization.data(withJSONObject: legacy), forKey: "jayjay.reviewedFiles") - - let store = ReviewStore(storeURL: url) - store.importLegacyMarks(from: defaults) - - XCTAssertTrue(store.isReviewed(changeId: "c1", path: "a.txt", identity: "idA")) - XCTAssertTrue(store.isHunkReviewed(changeId: "c1", path: "b.txt", identity: "idB", hunkIndex: 1)) - XCTAssertNil(defaults.data(forKey: "jayjay.reviewedFiles")) - - // A later run with an existing store file must not re-import or drop the blob. - try defaults.set(JSONSerialization.data(withJSONObject: legacy), forKey: "jayjay.reviewedFiles") - ReviewStore(storeURL: url).importLegacyMarks(from: defaults) - XCTAssertNotNil(defaults.data(forKey: "jayjay.reviewedFiles")) - } - func testMalformedStoreIsPreservedBeforeWrite() throws { let url = tempStoreURL() try FileManager.default.createDirectory(