feat(state): add scoped slice persistence#246
Conversation
📋 PR Overview
🔬 Coverage
|
adc1ab6 to
79e6688
Compare
b3d73e2 to
34dd607
Compare
79e6688 to
300911a
Compare
34dd607 to
12769b3
Compare
300911a to
3571eba
Compare
12769b3 to
6781e96
Compare
Co-authored-by: cooper <czxtm@users.noreply.github.com>
3571eba to
97359cf
Compare
6781e96 to
064cee2
Compare
| return section | ||
| .replace(/<!--[\s\S]*?-->/g, "") |
There was a problem hiding this comment.
Pull request overview
This PR advances the state-management migration by introducing a generic, typed Slice<T> backend primitive with pluggable JSON persistence (app-data vs repo-scoped), plus a slice registry that enables an auto-generated “tuning” UI driven by #[derive(Configurable)]. In parallel, it begins Phase 2 groundwork by adding a Diesel SQLite r2d2 pool and pool-backed helpers, and it updates PR hygiene checks (Danger + template) and planning docs.
Changes:
- Add
state::sliceinfrastructure (Slice<T>, persistence backends, registry) and initial slice-backed state (GlobalPreferences,EvolveState, repo-scopedEvolutionLimits). - Add developer settings auto-UI plumbing: configurable schema types, derive macro expansion, registry commands, and new React settings components + stories.
- Introduce Diesel pool scaffolding and pool-backed DB helpers (starting with commits), and adjust schema initialization strategy.
Reviewed changes
Copilot reviewed 44 out of 45 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/2026-05-29-state-management-migration-plan.md | New migration plan doc describing phased rollout of slices, scoped prefs, Diesel, and frontend split. |
| dangerfile.ts | Makes several Danger checks advisory; adds smarter Test Plan section parsing and reporting. |
| Cargo.lock | Updates Rust dependency graph (Diesel/r2d2 additions; lockfile churn). |
| apps/native/src/components/widget/settings/auto-tuning-section.tsx | New settings section that renders all backend-registered Configurable schemas. |
| apps/native/src/components/widget/settings/auto-tuning-section.stories.tsx | Storybook coverage for AutoTuningSection (happy path + error state). |
| apps/native/src/components/widget/settings/auto-config-field.tsx | New field renderer that maps ConfigField.ty.kind → UI controls and writes via devConfigs.set. |
| apps/native/src/components/widget/settings/auto-config-field.stories.tsx | Storybook coverage for AutoConfigField control variants. |
| apps/native/src/components/widget/settings/snapshots/auto-tuning-section.stories.tsx.snap | Snapshot baselines for new AutoTuningSection stories. |
| apps/native/src/components/widget/settings/snapshots/auto-config-field.stories.tsx.snap | Snapshot baseline for AutoConfigField story. |
| apps/native/src-tauri/src/storage/store.rs | Moves EvolutionLimits to repo-scoped store path and prefers managed slices when present; simplifies credential store usage. |
| apps/native/src-tauri/src/storage/mod.rs | Exposes configurable_scope module. |
| apps/native/src-tauri/src/storage/configurable_scope.rs | Adds .nixmac/settings.json repo-scoped path resolver + README bootstrap. |
| apps/native/src-tauri/src/state/watcher.rs | Changes watcher event model to slice-scoped events (and currently drops legacy git:status-changed). |
| apps/native/src-tauri/src/state/slice/registry.rs | New runtime registry of slice-backed configurable shims (schema + set_field). |
| apps/native/src-tauri/src/state/slice/persistence.rs | New persistence backends (AppDataJson, RepoScopedJson) behind a Persistence trait. |
| apps/native/src-tauri/src/state/slice/mod.rs | New Slice<T> + write-guard that emits events and flushes JSON on drop. |
| apps/native/src-tauri/src/state/slice/json_io.rs | Shared JSON read/write helpers used by persistence backends. |
| apps/native/src-tauri/src/state/preferences.rs | Introduces GlobalPreferences slice + VolatileJson fallback persistence. |
| apps/native/src-tauri/src/state/mod.rs | Documents new state module responsibilities; adds preferences + slice modules. |
| apps/native/src-tauri/src/state/evolve_state.rs | Migrates evolve routing state to slice-backed persistence + event emission. |
| apps/native/src-tauri/src/shared_types/settings_io.rs | Adds shared types for settings export/import result payloads. |
| apps/native/src-tauri/src/shared_types.rs | Re-exports new shared settings IO types. |
| apps/native/src-tauri/src/main.rs | Wires up managed slices/registry and new commands in GUI; adds slice setup in CLI path. |
| apps/native/src-tauri/src/evolve/config.rs | Converts EvolutionLimits into a repo-scoped, slice-backed configurable with registry registration. |
| apps/native/src-tauri/src/db/tables.rs | Adds Diesel table! definitions (starting with commits). |
| apps/native/src-tauri/src/db/schema.rs | Reworks schema init to recreate DB on version mismatch (no rusqlite_migration). |
| apps/native/src-tauri/src/db/pool.rs | Adds Diesel SQLite pool builder (DbPool). |
| apps/native/src-tauri/src/db/mod.rs | Initializes and manages the Diesel pool in Tauri state; adds pool init helper + test. |
| apps/native/src-tauri/src/db/commits.rs | Adds Diesel pool-backed commit upsert/query helpers alongside rusqlite path. |
| apps/native/src-tauri/src/commands/settings_io.rs | Adds export/import commands for slice-backed settings with secret filtering. |
| apps/native/src-tauri/src/commands/mod.rs | Registers new command modules (dev_configs, settings_io). |
| apps/native/src-tauri/src/commands/dev_configs.rs | Adds commands to enumerate schemas + set fields via slice registry. |
| apps/native/src-tauri/src/commands/config.rs | Removes legacy host-attr fallback read from ~/.config/darwin/host. |
| apps/native/src-tauri/examples/specta_gen_ts.rs | Updates specta registrations (GitState, settings IO, configurable schema types). |
| apps/native/src-tauri/configurable/src/lib.rs | Expands runtime crate to include configurable schema types (specta-exportable). |
| apps/native/src-tauri/configurable/Cargo.toml | Adds specta derive; still includes now-unused tauri/store deps (see comments). |
| apps/native/src-tauri/configurable-derive/src/types.rs | New derive helper mapping Rust types/attrs to UI schema field types. |
| apps/native/src-tauri/configurable-derive/src/strings.rs | New string helpers (snake→camel + humanize). |
| apps/native/src-tauri/configurable-derive/src/lib.rs | Refactors derive entrypoint to delegate to new codegen pipeline. |
| apps/native/src-tauri/configurable-derive/src/fields.rs | New per-field codegen/validation building blocks. |
| apps/native/src-tauri/configurable-derive/src/codegen.rs | New derive codegen pipeline generating load/schema/setters + Wry shims. |
| apps/native/src-tauri/configurable-derive/src/attrs.rs | New struct/field attribute parsing with scope + UI metadata. |
| apps/native/src-tauri/Cargo.toml | Adds Diesel/r2d2 deps; removes git2 (currently breaks compilation—see comments). |
| .github/PULL_REQUEST_TEMPLATE.md | Adds “No test plan needed” checkbox. |
| .beads/issues.json | Adds/updates Beads issue tracking entries for migration phases. |
| let app = match tauri::Builder::default() | ||
| // Ensure store plugin (and its managed state) is initialized so we can load settings | ||
| .plugin(tauri_plugin_store::Builder::default().build()) | ||
| .plugin(tauri_plugin_keyring::init()) | ||
| .invoke_handler(tauri::generate_handler![]) | ||
| .setup(|_app| Ok(())) | ||
| .setup(|app| { | ||
| app.manage(state::preferences::load_global_slice(app.handle())?); | ||
| app.manage(evolve::config::load_slice(app.handle())?); | ||
| evolve::config::register_slice_config( | ||
| &app.state::<state::slice::SliceRegistry>(), | ||
| )?; | ||
| app.manage(state::evolve_state::load_slice(app.handle())?); |
| if let Ok(es) = evolve_state::get(&app_handle) { | ||
| // fire-and-forget: cache update in polling loop. | ||
| let _ = evolve_state::set(&app_handle, es, &status.changes); | ||
| } | ||
| // fire-and-forget: frontend event delivery; window may not be connected. | ||
| let _ = app_handle.emit( | ||
| "git:status-changed", | ||
| WatcherEvent { | ||
| "git_state_changed", | ||
| GitState { | ||
| git_status: Some(status.clone()), | ||
| change_map: Some(change_map), | ||
| evolve_state: evolve_state_updated, | ||
| error: None, | ||
| external_build_detected, | ||
| }, | ||
| ); | ||
| let _ = app_handle.emit("change_map_changed", change_map); |
| Err(e) => { | ||
| // fire-and-forget: error event delivery to frontend. | ||
| let _ = app_handle.emit( | ||
| "git:status-changed", | ||
| WatcherEvent { | ||
| git_status: None, | ||
| change_map: None, | ||
| evolve_state: None, | ||
| error: Some(e.to_string()), | ||
| external_build_detected: false, | ||
| }, | ||
| ); | ||
| let _ = app_handle.emit("git_state_error", e.to_string()); | ||
| } |
| const SCHEMA_VERSION: i32 = 1; | ||
| const CURRENT_SCHEMA_SQL: &str = concat!( | ||
| include_str!("../../migrations/01-initial/up.sql"), | ||
| "\n", | ||
| include_str!("../../migrations/02-restore-commits/up.sql"), | ||
| "\nPRAGMA user_version = 1;\n", | ||
| ); |
| conn.execute_batch( | ||
| r#" | ||
| CREATE TABLE evolutions ( | ||
| id INTEGER PRIMARY KEY, | ||
| branch TEXT NOT NULL, | ||
| merged INTEGER NOT NULL DEFAULT 0, | ||
| builds INTEGER NOT NULL DEFAULT 0 | ||
| ); | ||
| INSERT INTO evolutions (id, branch, merged, builds) VALUES (1, 'main', 0, 0); | ||
| PRAGMA user_version = 3; | ||
| "#, |
| rnix = "0.14.0" | ||
| serde_yaml = "0.9" | ||
| rowan = "0.16.1" | ||
| fuzzy-matcher = "0.3.7" | ||
| tauri-plugin-webdriver-automation = "0.1.3" | ||
| tiktoken-rs = "0.6" | ||
| configurable = { path = "configurable" } | ||
|
|
| import { tauriAPI } from "@/ipc/api"; | ||
| import type { ConfigurableSchema } from "@/ipc/types"; | ||
| import { SlidersHorizontal } from "lucide-react"; |
| import { tauriAPI } from "@/ipc/api"; | ||
| import type { ConfigField } from "@/ipc/types"; | ||
| import { Info } from "lucide-react"; | ||
| import { useState } from "react"; |
| import { AutoTuningSection } from "@/components/widget/settings/auto-tuning-section"; | ||
| import { tauriAPI } from "@/ipc/api"; | ||
| import type { ConfigurableSchema } from "@/ipc/types"; | ||
| import { waitFor, within } from "storybook/test"; |
| import { AutoConfigField } from "@/components/widget/settings/auto-config-field"; | ||
| import { tauriAPI } from "@/ipc/api"; | ||
| import type { ConfigField } from "@/ipc/types"; | ||
|
|

Summary
Test Plan
Docs