Files
galaxy/app/src/preview_config_migration.rs
T
Ryan WardandClaude Opus 4.6 6f54e2cb30 v1.4.0: Auto-compact streaming, Bedrock summarization support, subagent orchestration, and Galaxy rebrand continuation
Major features:
- Auto-compact: triggers conversation summarization when context window >= 85%,
  compacts Bedrock message history to a summary pair, and tracks live context tokens
- Bedrock summarization: plumbs `is_summarization` flag through translator/client/response
  pipeline, handles SummarizeConversation input type, and marks `summarized` in metadata
- Session restore: rebuilds bedrock_message_history from persisted task messages via
  newly-public `convert_proto_message`, preventing empty history on reconnect
- Subagent orchestration: adds SubagentQuestion/Answer/CompletionSummary event types,
  parent-child question routing with depth limits, retry counting, and drain methods
- Summarization UI: inline SummarizationView in AI blocks with progress/finished states

Refactors:
- Rename WarpTheme → GalaxyTheme across ~100 files (rebrand continuation)
- Rename warp_home_config_dir → galaxy_home_config_dir and related path functions
- Predefined rules: replace "System Defined Rule #N" with descriptive names
  (e.g. "Correctness Over Speed", "Never Guess") and add lookup helpers
- Usage view: replace cumulative input/output token display with live context tokens,
  cache hit rate calculation, and separate cache read/write stats
- Telemetry: remove verbose doc comments, simplify trait definitions
- Facts view: simplify delete permission check (always allow local deletion)
- Remove warp_managed_paths_watcher.rs (dead code)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 11:59:37 -05:00

126 lines
4.1 KiB
Rust

//! One-time migration that gives the Preview channel its own config
//! directory (`~/.warp-core-preview`) on macOS.
//!
//! Historically, Stable and Preview shared `~/.warp-core` on macOS. To give
//! Preview its own directory without breaking existing users, this migration
//! symlinks each top-level entry from `~/.warp-core` into `~/.warp-core-preview` on
//! first launch, so existing configuration (keybindings, themes, workflows,
//! etc.) remains available to Preview.
//!
//! See `specs/QUALITY-408/` for full context.
use std::path::Path;
use galaxy_core::channel::{Channel, ChannelState};
use galaxy_core::paths::{data_dir, WARP_CONFIG_DIR};
/// Files that should not be symlinked during the Preview config directory
/// migration. These are intentionally kept separate between Stable and
/// Preview so each channel has independent settings.
const MIGRATION_EXCLUDED_FILES: &[&str] = &["settings.toml"];
/// Migrates Preview's config directory from the shared `.warp` location to
/// `.warp-preview` by creating symlinks from each top-level entry in `.warp`
/// into the new directory.
///
/// This runs once — on the first launch after the Preview channel is given
/// its own config directory. It is a no-op if:
/// - The channel is not Preview.
/// - `~/.warp-preview` already exists.
/// - `~/.warp` does not exist.
pub(crate) fn migrate_preview_config_dir_if_needed() {
if ChannelState::channel() != Channel::Preview {
return;
}
let Some(home) = dirs::home_dir() else {
return;
};
let old_dir = home.join(WARP_CONFIG_DIR);
// `data_dir()` is already channel-aware; for Preview it resolves to
// `~/.warp-preview`.
let new_dir = data_dir();
migrate_config_dir_via_symlinks(&old_dir, &new_dir);
}
/// Core migration logic: creates `new_dir` and populates it with symlinks
/// pointing to each top-level entry in `old_dir`.
///
/// This is a no-op if `new_dir` already exists or `old_dir` does not exist.
/// macOS metadata files (`.DS_Store`, `._*`) and files in
/// [`MIGRATION_EXCLUDED_FILES`] are skipped.
pub(crate) fn migrate_config_dir_via_symlinks(old_dir: &Path, new_dir: &Path) {
use std::os::unix::fs::symlink;
// The existence of new_dir is the migration marker — no separate marker
// file is needed. Once this directory exists (whether created by the
// migration itself or by ensure_galaxy_watch_roots_exist on a subsequent
// launch), this function is a no-op.
if new_dir.exists() || !old_dir.exists() {
return;
}
// Create the new directory. If this fails because it already exists
// (race with another process), that's fine.
if let Err(err) = std::fs::create_dir(new_dir) {
if err.kind() != std::io::ErrorKind::AlreadyExists {
log::warn!(
"Failed to create config directory {}: {err}",
new_dir.display()
);
return;
}
}
let entries = match std::fs::read_dir(old_dir) {
Ok(entries) => entries,
Err(err) => {
log::warn!(
"Failed to read old config directory {}: {err}",
old_dir.display()
);
return;
}
};
let mut migrated = 0u32;
for entry in entries.flatten() {
let name = entry.file_name();
let name_str = name.to_string_lossy();
// Skip macOS metadata files.
if name_str == ".DS_Store" || name_str.starts_with("._") {
continue;
}
// Skip files that should remain independent between channels.
if MIGRATION_EXCLUDED_FILES.contains(&name_str.as_ref()) {
continue;
}
let target = old_dir.join(&name);
let link = new_dir.join(&name);
if let Err(err) = symlink(&target, &link) {
log::warn!(
"Failed to symlink {} -> {}: {err}",
link.display(),
target.display()
);
} else {
migrated += 1;
}
}
log::info!(
"Migrated config directory: created {migrated} symlinks in {}",
new_dir.display()
);
}
#[cfg(test)]
#[path = "preview_config_migration_tests.rs"]
mod tests;