219 files, 2.0 MB, untracked in souveraine/docs and existing nowhere else. The volume is at 100% with no snapshots.
3.9 KiB
Config → Settings Sync Architecture
How the Settings UI (TOML editor) pushes changes to other data stores. Last updated: 2026-05-14
The two persistence systems
System A — souveraine.toml (config file)
User-editable fields across ~20 sections (bifrost, subconscious, compaction, memory, voice, etc.).
Edited by Settings UI (src/ui/settings.rs), saved to disk, hot-loaded into Arc<RwLock<ConsciousnessConfig>>.
System B — SQLite agent records (~/.souveraine/server/agents/*/)
Primary agent state including llm_model, context_window, temperature, tags, etc.
Read by run_turn at src/backend/local.rs:793 for the per-turn Bifrost request.
The model value in System B was written once at agent creation time and never updated.
The sync rule
On every Settings save (TOML write), compute the diff between the original snapshot and the saved config. For each changed field, push the new value to every data store that shadows it:
TOML field → SQLite column
─────────────────────────────────────────────────────
bifrost.primary_model → agents.llm_model
subconscious.model → subconscious agent's llm_model (future)
reflection.model → subconscious agent's reflection config (future)
compaction.* → agent config_json (future)
memory.* → agent config_json (future)
Current implementation scope (2026-05-14): only primary_model syncs to SQLite.
The diff-and-push pattern is extensible; add new field mappings by extending the sync block.
The sync path
SettingsView::handle_key('y') → SettingsAction::Save
└─ App::handle_settings_key()
├─ view.save(&path) → writes TOML to disk
├─ live = view.config.clone() → hot-reloads in-memory config
└─ sync_fields(view) → pushes changed fields to SQLite
└─ for each (field, new_val) in changed_fields()
└─ backend.update_agent_model(id, val)
└─ AgentInventory::update() → SQLite UPDATE
→ agent.json rewrite
→ cache refresh
The function sync_fields() computes the diff between view.original (snapshot at Settings
entry) and view.config (edited state). Only fields whose values actually changed are pushed,
which makes the system idempotent — saving the same config twice writes TOML twice but
skips redundant SQLite writes.
Multi-field change guard (stub)
When multiple fields with SQLite shadows change in a single save, the system should prompt for confirmation before pushing all of them. This prevents accidental bulk updates (e.g. editing both primary_model and subconscious.model at once without realizing both affect agent records).
Implementation status: stub. The diff machinery enumerates changed fields but the confirmation dialog is not yet wired. Change count is logged but save proceeds without blocking:
tracing::info!(
changed_fields = %field_list,
"Settings sync: pushing N fields to SQLite"
)
Relevant TOML fields that will eventually need this guard:
| Section | Field | Synced to |
|---|---|---|
bifrost |
primary_model |
agents.llm_model |
subconscious |
model |
subconscious agent |
compaction.per_type.* |
strategy, thresholds | agent config_json |
memory |
base_path, git_enabled | agent config_json |
One sync per save
Each Settings save triggers exactly one sync pass. The sync is synchronous within the save handler — the TUI shows "saved" only after both TOML write and SQLite writes complete. If a SQLite write fails, the error is logged but the TOML save is not rolled back (the user can retry by saving again).