feat(config): migrate 모듈 스캐폴딩 + toml_edit 의존성

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-31 11:41:32 +00:00
parent 6d86214060
commit 4dcb4a45d6
4 changed files with 50 additions and 0 deletions

1
Cargo.lock generated
View File

@@ -4371,6 +4371,7 @@ dependencies = [
"tempfile", "tempfile",
"thiserror 2.0.18", "thiserror 2.0.18",
"toml", "toml",
"toml_edit 0.22.27",
"tracing", "tracing",
] ]

View File

@@ -15,6 +15,7 @@ serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
toml = "0.8" toml = "0.8"
toml_edit = "0.22"
dirs = "5" dirs = "5"
# p9-fb-05: warn-log when current_dir() fails (chroot, deleted cwd) # p9-fb-05: warn-log when current_dir() fails (chroot, deleted cwd)
# during workspace.root resolution. # during workspace.root resolution.

View File

@@ -9,6 +9,7 @@ use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
mod paths; mod paths;
pub mod migrate;
pub use paths::{expand_path, expand_path_with_base}; pub use paths::{expand_path, expand_path_with_base};
/// Signal: `Config::from_file` / `Config::load` failed due to missing path, /// Signal: `Config::from_file` / `Config::load` failed due to missing path,

View File

@@ -0,0 +1,47 @@
//! config.toml 마이그레이션 엔진 (순수 변환, I/O 없음).
//!
//! 두 메커니즘: (1) reconciliation — default 구조에 있고 사용자 파일에
//! 없는 섹션/키를 주석과 함께 추가. (2) step 체인 — schema_version 기반
//! non-additive 변환(deprecated 제거 등). 자세한 계약은 spec
//! `docs/superpowers/specs/2026-05-31-config-migration-design.md`.
use serde::Serialize;
/// 현재 바이너리가 이해하는 config 스키마 버전. 마이그레이션 완료 시
/// 사용자 파일의 `schema_version` 을 이 값으로 stamp 한다.
pub const CURRENT_SCHEMA_VERSION: u32 = 2;
/// 한 번의 마이그레이션에서 발생한 개별 변경.
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct MigrationChange {
pub kind: ChangeKind,
/// dotted path, 예: `ingest.expansion`, `workspace.include`.
pub path: String,
/// 사람·wire 용 한 줄 설명.
pub detail: String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ChangeKind {
AddedSection,
AddedKey,
RemovedDeprecated,
}
/// 마이그레이션 결과 요약(순수 변환 단계 산출). I/O 계층이 backup_path
/// 등을 채워 wire 로 내보낸다.
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct MigrationOutcome {
pub from_schema_version: u32,
pub to_schema_version: u32,
pub changes: Vec<MigrationChange>,
/// 변환 후 직렬화된 새 문서 텍스트(멱등 시 입력과 동일).
pub new_text: String,
}
impl MigrationOutcome {
pub fn changed(&self) -> bool {
!self.changes.is_empty()
}
}