feat(kebab-config + kebab-app): p9-fb-05 workspace.root path policy #76

Merged
altair823 merged 2 commits from feat/p9-fb-05-path into main 2026-05-03 04:23:33 +00:00
Owner

요약

도그푸딩 item 3 — workspace.root 의 허용 형식이 명문화 안 돼 사용자가 "상대 경로면 어디 기준?" 가 불명확. 절대/tilde/env/상대 모두 지원하되 상대 경로의 base 는 config.toml 자체가 위치한 디렉토리 (cwd 와 무관) 정책 도입.

변경

  • kebab_config::expand_path_with_base(raw, data_dir, base_dir) 신규 — relative path 만 base_dir 에 join, 절대/tilde/env 는 그대로
  • Config.source_dir: Option<PathBuf> (#[serde(skip)]) — from_file / loadpath.parent() stamp
  • Config::resolve_workspace_root() helper — 모든 callsite 통일
  • kebab-app + kebab-source-fs 의 expand_tilde(&workspace.root) 4 군데 → cfg.resolve_workspace_root()
  • kebab-source-fs 의 fork 된 expand_tilde + dirs_home 헬퍼 제거
  • kebab init 가 생성하는 config.toml 위에 path policy 안내 헤더 코멘트 prepend

테스트

  • 신규 4 unit (expand_path_with_base: relative→base, absolute / tilde / ${VAR} 는 ignore base)
  • 27 kebab-config 통과
  • cargo test --workspace --no-fail-fast -j 1 exit 0
  • cargo clippy --workspace --all-targets -- -D warnings clean

문서

  • README Configuration: workspace.root 형식 + relative base 규칙 한 줄
  • HANDOFF: 2026-05-03 entry
  • spec status planned → in_progress

영향

기존 사용자 (defaults ~/KnowledgeBase = tilde-rooted) 영향 없음. 새 사용자가 --config /tmp/cfg.toml + root = \"kb\" 쓰면 cwd 무관하게 /tmp/kb 가 워크스페이스 — 이전엔 cwd 기준이라 invisible foot-gun.

Out of scope

  • expand_tilde 통일 (kebab-app 의 storage.data_dir 한 군데 남음) — spec out-of-scope, P+
## 요약 도그푸딩 item 3 — `workspace.root` 의 허용 형식이 명문화 안 돼 사용자가 \"상대 경로면 어디 기준?\" 가 불명확. 절대/tilde/env/상대 모두 지원하되 **상대 경로의 base 는 config.toml 자체가 위치한 디렉토리** (cwd 와 무관) 정책 도입. ## 변경 - **`kebab_config::expand_path_with_base(raw, data_dir, base_dir)`** 신규 — relative path 만 base_dir 에 join, 절대/tilde/env 는 그대로 - **`Config.source_dir: Option<PathBuf>`** (`#[serde(skip)]`) — `from_file` / `load` 가 `path.parent()` stamp - **`Config::resolve_workspace_root()`** helper — 모든 callsite 통일 - kebab-app + kebab-source-fs 의 `expand_tilde(&workspace.root)` 4 군데 → `cfg.resolve_workspace_root()` - kebab-source-fs 의 fork 된 `expand_tilde` + `dirs_home` 헬퍼 제거 - `kebab init` 가 생성하는 `config.toml` 위에 path policy 안내 헤더 코멘트 prepend ## 테스트 - 신규 4 unit (`expand_path_with_base`: relative→base, absolute / tilde / `${VAR}` 는 ignore base) - 27 kebab-config 통과 - `cargo test --workspace --no-fail-fast -j 1` exit 0 - `cargo clippy --workspace --all-targets -- -D warnings` clean ## 문서 - README Configuration: workspace.root 형식 + relative base 규칙 한 줄 - HANDOFF: 2026-05-03 entry - spec status planned → in_progress ## 영향 기존 사용자 (defaults `~/KnowledgeBase` = tilde-rooted) 영향 없음. 새 사용자가 `--config /tmp/cfg.toml` + `root = \"kb\"` 쓰면 cwd 무관하게 `/tmp/kb` 가 워크스페이스 — 이전엔 cwd 기준이라 invisible foot-gun. ## Out of scope - `expand_tilde` 통일 (kebab-app 의 `storage.data_dir` 한 군데 남음) — spec out-of-scope, P+
altair823 added 1 commit 2026-05-03 04:20:37 +00:00
도그푸딩 item 3 — `workspace.root` 의 허용 형식이 명문화 안 돼 사용자가
\"상대 경로면 어디 기준?\" 가 불명확. 이제 절대/tilde/env/상대 모두
지원하되, 상대 경로의 base 는 **config.toml 자체가 위치한 디렉토리**
(사용자의 cwd 와 무관) 로 일관 정책.

## 핵심 변경

- **`kebab_config::expand_path_with_base(raw, data_dir, base_dir)`**
  신규. 기존 `expand_path` (tilde + env 만) 위에 relative-path
  resolution 추가:
  - tilde / 절대 / `${VAR}` 입력은 base_dir 무시 (이미 absolute)
  - relative 입력만 `base_dir.join(...)` 로 절대화
- **`Config.source_dir: Option<PathBuf>`** 신규 (`#[serde(skip)]`).
  `Config::from_file` / `load` 가 `path.parent()` 로 stamp. defaults
  는 None (cwd fallback).
- **`Config::resolve_workspace_root()`** helper: source_dir 있으면
  그것 기준, 없으면 cwd 기준.
- **callsite 정리**:
  - `kebab-app::lib.rs` 의 3 군데 `expand_tilde(&app.config.workspace
    .root)` → `app.config.resolve_workspace_root()`
  - `kebab-app::init_workspace` 도 동일
  - `kebab-source-fs::FsSourceConnector::new` → 동일
  - kebab-source-fs 의 fork 된 local `expand_tilde` + `dirs_home`
    헬퍼 제거 (kebab-config 가 canonical)
- **`kebab init`** 가 생성하는 `config.toml` 위에 path policy 안내
  헤더 코멘트 prepend (절대/tilde/env/상대 + 상대 base = config dir).

기존 `expand_tilde` 가 kebab-app/lib.rs 에 한 군데 (storage.data_dir)
남음 — spec out-of-scope (\"expand_tilde 통일 P+\") 라 보류.

## 테스트

- `expand_path_with_base` 에 신규 4 unit (relative→base, absolute
  ignores base, tilde ignores base, ${XDG} ignores base)
- 기존 27 kebab-config tests + workspace 전체 (`cargo test --workspace
  --no-fail-fast -j 1` exit 0) 모두 통과
- `cargo clippy --workspace --all-targets -- -D warnings` clean

## 문서

- README Configuration 절: workspace.root 형식 + relative base 규칙
  한 줄 추가
- HANDOFF: 2026-05-03 entry
- spec status planned → in_progress

## 영향

기존 사용자: 영향 없음 (defaults 의 `~/KnowledgeBase` 는 tilde-rooted,
relative path 분기 안 탐). 새 사용자가 `--config /tmp/cfg.toml` +
`root = "kb"` 같이 쓰면 cwd 무관하게 `/tmp/kb` 가 워크스페이스가 됨 —
이전엔 이 케이스가 cwd 기준이라 invisible foot-gun.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 requested changes 2026-05-03 04:21:41 +00:00
Dismissed
claude-reviewer-01 left a comment
Member

회차 1 — path policy 정책 자체와 구현 모두 깔끔. expand_path_with_base 가 기존 expand_path 위에 if-absolute 한 줄만 추가 = 위험 적고 검증 쉬움. Config.source_dir 의 #[serde(skip)] + from_file/load stamp 패턴 정석. kebab init 의 header prepend 로 사용자 발견성도 chase.

actionable nit 2 건 — (a) Config.source_dir 의 pub mutability 가 invariant 보호 안 함, (b) current_dir() 실패 시 silently '.' 로 떨어지는 fallback 의 가시성 부족.

회차 1 — path policy 정책 자체와 구현 모두 깔끔. expand_path_with_base 가 기존 expand_path 위에 if-absolute 한 줄만 추가 = 위험 적고 검증 쉬움. Config.source_dir 의 #[serde(skip)] + from_file/load stamp 패턴 정석. kebab init 의 header prepend 로 사용자 발견성도 chase. actionable nit 2 건 — (a) Config.source_dir 의 pub mutability 가 invariant 보호 안 함, (b) current_dir() 실패 시 silently '.' 로 떨어지는 fallback 의 가시성 부족.
@@ -35,0 +38,4 @@
/// `expand_path_with_base` to resolve relative `workspace.root`
/// against the config file's location instead of the user's
/// `cwd` (so `--config /tmp/cfg.toml` + `root = "kb"` reads
/// `/tmp/kb` no matter where the user invoked from).

source_dirpub 이라 외부 코드가 직접 mutate 할 수 있는데, from_file / load 가 stamp 하는 invariant 를 깨면 resolve_workspace_root() 가 잘못된 base 를 쓰게 됩니다. 두 옵션:

  1. pub(crate) 로 좁히고 pub fn source_dir(&self) -> Option<&Path> getter + pub fn with_source_dir(self, dir: PathBuf) -> Self builder 노출 (테스트 / 프로그래마틱 사용에는 builder).
  2. pub 그대로 두되 doc 마지막에 "외부 코드는 직접 set 하지 말 것; from_file / load 만이 정당한 setter" 한 줄 추가.

저는 (1) 이 invariant 보호 측면에서 더 명확하다고 봅니다 — serde(skip) 라 외부 source 는 from_file 가 stamp 하지 않은 채로는 절대 set 될 수 없어야 함.

`source_dir` 가 `pub` 이라 외부 코드가 직접 mutate 할 수 있는데, `from_file` / `load` 가 stamp 하는 invariant 를 깨면 `resolve_workspace_root()` 가 잘못된 base 를 쓰게 됩니다. 두 옵션: 1. `pub(crate)` 로 좁히고 `pub fn source_dir(&self) -> Option<&Path>` getter + `pub fn with_source_dir(self, dir: PathBuf) -> Self` builder 노출 (테스트 / 프로그래마틱 사용에는 builder). 2. `pub` 그대로 두되 doc 마지막에 "외부 코드는 직접 set 하지 말 것; `from_file` / `load` 만이 정당한 setter" 한 줄 추가. 저는 (1) 이 invariant 보호 측면에서 더 명확하다고 봅니다 — `serde(skip)` 라 외부 source 는 `from_file` 가 stamp 하지 않은 채로는 절대 set 될 수 없어야 함.

std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))"." fallback 이 silently 실패합니다 — . + relative root = ./<root> 가 되고, 이후 std::fs::create_dir_all 등이 cwd 기준으로 해석하긴 하지만 "cwd 가 reading 실패" 라는 진짜 에러는 사라집니다.

fallback 자체는 합리적이지만 (panic 보다는 낫다), tracing::warn! 한 줄 추가하면 사용자가 환경 이상 (chroot, deleted cwd 등) 일 때 silently 잘못된 경로로 빠지는 걸 발견할 수 있습니다:

.unwrap_or_else(|e| {
    tracing::warn!(target: "kebab-config", error = %e, "current_dir() failed; falling back to '.' for workspace.root resolution");
    PathBuf::from(".")
})
`std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))` 의 `"."` fallback 이 silently 실패합니다 — `.` + relative root = `./<root>` 가 되고, 이후 `std::fs::create_dir_all` 등이 `cwd` 기준으로 해석하긴 하지만 "cwd 가 reading 실패" 라는 진짜 에러는 사라집니다. fallback 자체는 합리적이지만 (panic 보다는 낫다), `tracing::warn!` 한 줄 추가하면 사용자가 환경 이상 (chroot, deleted cwd 등) 일 때 silently 잘못된 경로로 빠지는 걸 발견할 수 있습니다: ```rust .unwrap_or_else(|e| { tracing::warn!(target: "kebab-config", error = %e, "current_dir() failed; falling back to '.' for workspace.root resolution"); PathBuf::from(".") }) ```
altair823 added 1 commit 2026-05-03 04:23:20 +00:00
- `Config.source_dir` 를 `pub(crate)` 로 좁힘. invariant ("from_file
  / load 만이 정당한 setter") 가 외부 mutation 으로 깨지지 않도록.
  대신 `pub fn source_dir(&self) -> Option<&Path>` (read-only) +
  `pub fn with_source_dir(self, dir) -> Self` (builder) 노출 — 테스트
  / 프로그래마틱 사용은 builder 통과.
- `resolve_workspace_root` 의 `current_dir()` 실패 fallback 에
  `tracing::warn!` 추가. chroot / deleted-cwd / permission 문제로
  cwd 가 안 잡힐 때 silently `./root` 로 떨어지지 않고 로그가 남음.
  `tracing` 을 kebab-config 의 deps 에 추가 (workspace dep).

테스트 27 통과 + 워크스페이스 clippy clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 approved these changes 2026-05-03 04:23:28 +00:00
claude-reviewer-01 left a comment
Member

회차 2 — nit 2 건 깔끔히 반영.

  • source_dir → pub(crate), source_dir() 읽기 + with_source_dir() 빌더 노출
  • current_dir() 실패 fallback 에 tracing::warn! 추가 (kebab-config deps 에 tracing workspace dep 도 추가)

추가 지적 없음. 머지 OK.

회차 2 — nit 2 건 깔끔히 반영. - source_dir → pub(crate), source_dir() 읽기 + with_source_dir() 빌더 노출 - current_dir() 실패 fallback 에 tracing::warn! 추가 (kebab-config deps 에 tracing workspace dep 도 추가) 추가 지적 없음. 머지 OK.
altair823 merged commit 18a1c968f0 into main 2026-05-03 04:23:33 +00:00
altair823 deleted branch feat/p9-fb-05-path 2026-05-03 04:23:34 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/kebab#76