---
baseline: N0011@r0000
created: 2026-04-09T15:50:56.253870+00:00
---

# v0.1 plan — fix 사항 정리

> 기준 문서: [PLAN_v0.1.md](PLAN_v0.1.md)
> 작성일: 2026-04-07

---

## 결정 완료 항목

### fix-001: serde_yaml 제거 — frontmatter 직접 파싱

**문제:** `serde_yaml` 0.9는 사실상 유지보수 종료 상태.

**결정:** 직접 구현. frontmatter 구조가 `id`, `title`, `baseline`, `tags` 4개 필드로 단순하므로
`---\n...\n---` 경계 기준 split 후 key-value 줄 단위 파싱으로 충분.

**영향:** `Cargo.toml`에서 `serde_yaml` 제거. `schema/manifest.rs`의 `NoteFrontmatter` 파서 자체 구현.

---

### fix-002: thiserror 버전 통일

**문제:** ROADMAP은 `"1"`, PLAN은 `"2"`. v2는 `#[error(transparent)]` 동작이 변경됨.

**결정:** `thiserror = "1"` 고정. 생태계 안정성 우선.

**영향:** `Cargo.toml` 통일.

---

### fix-003: --dry-run 모듈별 독립 구현

**문제:** Phase 2(init)에서 구현하고 Phase 7에서 "전체 일관 적용"을 다시 목표로 하여 구현 시점 중복.

**결정:** 각 커맨드 모듈에서 독립 구현. `init`은 Phase 2에서, `entry new` / `link`는 각 Phase에서 완결.
Phase 7은 `--json` 및 structured error polish에 집중.

---

### fix-004: 공유 유틸리티 — 점진적 추가, 충돌 시 검토 요청

**문제:** 원자적 쓰기(`atomic_write`)와 `sync.jsonl` append가 공유 유틸리티로 정의되지 않은 채 각 Phase에 분산.

**결정:**
- 필요한 Phase에서 먼저 구현하고, 중복 발생 시 `vault/util.rs`로 추출.
- 추출 시점이 불분명하거나 인터페이스 충돌이 예상되면 진행 전 검토 요청.

**구체적 대상:**
- `atomic_write(path, content)` — Phase 3(entry new) 또는 Phase 5(link) 중 먼저 필요한 시점에 구현
- `append_sync_event(vault_root, event)` — Phase 3(entry new)에서 구현, 이후 재사용

---

### fix-005: claude.md v0.1 전용 내용으로 교체

**문제:** Phase 2가 생성하는 CLAUDE.md 상수에 `elf help --json`, `elf sync record`가 포함되어 있으나
두 명령 모두 v0.2 구현 예정. v0.1 vault에서 실행하면 오류 발생.

**결정:** `src/cli/init.rs`의 `CLAUDE_MD` 상수를 v0.1 기준으로 교체.

```rust
const CLAUDE_MD_V0_1: &str = r#"# Elendirna vault

이 저장소는 `elf` CLI로만 수정합니다. 직접 파일 편집 금지.
사용 가능한 명령: entry new / edit / show, revision add, link, validate (--help 참고).
스키마/규칙 위반은 `elf validate`가 보고합니다 — 에러의 `fix` 필드를 따르면 됩니다.
"#;
```

v0.2 업그레이드 시 `elf migrate`가 CLAUDE.md를 재생성하거나, `elf init --upgrade`로 갱신.

---

### fix-006: entry new 멱등성 기준 — slug 충돌 기준으로 변경

**문제:** "동일 title로 재호출" 기준이 불명확 (`"Rust Ownership"` vs `"rust ownership"` 처리 미정).

**결정:**
- 비교는 CI(case-insensitive) title 기준.
- 동일 slug가 생성되는 경우 (`N####_<slug>` 디렉터리 충돌) → `E3001 already_exists`, 기존 entry 경로 반환.
- slug 충돌을 1차 기준으로 삼고, title은 CI 정규화 후 참고 검사.

---

### fix-012: 에러 코드 중앙화

**문제:** `E2001`, `E2002`, `E3001`, `E4002`, `E4003` 등이 각 cmd 문서에 산재.
코드 레벨에서 불일치 발생 가능.

**결정:** `error.rs`에 에러 코드 enum + 코드 문자열 registry를 정의하고 모든 커맨드가 이를 참조.

```rust
pub enum ElfErrorCode {
    NotAVault,          // E2001
    NotFound,           // E2002
    AlreadyExists,      // E3001
    EditorNotSet,       // E4002
    ParseError,         // E4003
    IoError,            // E4000
    SchemaVersionMismatch, // E5001
}
```

---

### fix-013: sync.jsonl agent 필드 기본값

**문제:** `sync.jsonl`의 `agent` 필드가 CLI 직접 호출 시 어떻게 채워지는지 미정.

**결정:** v0.1에서는 `"human"` 고정. 환경 변수 `ELF_AGENT`가 설정되어 있으면 그 값 사용.

```jsonl
{"ts": "...", "agent": "human", "action": "entry.new", "id": "N0042"}
```

v0.2의 `elf sync record`에서 에이전트 식별을 공식화.

---

### fix-014: entry show --json의 note 필드 범위

**문제:** `--json` 출력 시 `note` 필드가 frontmatter 포함인지 본문만인지 불명확.
frontmatter는 manifest와 중복.

**결정:** `note` 필드는 frontmatter를 제외한 **본문만** 반환.

```json
{
  "manifest": { "id": "N0042", "title": "...", ... },
  "note": "# Rust ownership\n\n본문 내용..."
}
```

frontmatter는 manifest에서 이미 구조화되어 있으므로 중복 노출 불필요.

---

### fix-015: --json 모드에서 stderr 에러 형식 명시

**문제:** `--json` 플래그가 켜졌을 때 에러도 JSON으로 가야 하는지 명시 없음.

**결정:** `--json` 플래그와 무관하게 에러는 항상 JSON으로 stderr 출력.
이유: 에이전트가 `--json` 없이도 에러를 파싱할 수 있어야 Agent I/O Contract 충족.

```
stdout: 정상 출력 (--json 여부에 따라 pretty/JSON)
stderr: 항상 JSON 형식 에러 (README §6.2 구조)
```

---

## 설계 결정 필요 항목

### fix-007: entry edit 후 동기화 방향

**문제:** 사용자가 `$EDITOR`에서 frontmatter를 직접 수정했을 때의 처리 방식 미정.
README는 "manifest가 SSoT"이지만, `entry edit`은 사용자가 frontmatter를 직접 수정할 수 있는 유일한 경로.

**선택지:**

| 방식 | 동작 | 장점 | 단점 |
|------|------|------|------|
| A: 경고만 | 불일치 시 WARN 출력, validate에서 잡음 | manifest SSoT 원칙 엄수 | 사용자가 frontmatter를 고쳐도 반영 안 됨 |
| B: frontmatter → manifest 역반영 | 편집 후 frontmatter 변경분을 manifest에 자동 반영 | UX 자연스러움 | SSoT 방향 역전 |
| C: 선택적 반영 | 불일치 감지 시 `[y/N] manifest를 frontmatter로 갱신?` 프롬프트 | 유연 | --json 모드에서 처리 복잡 |

> **미결정** — 결정 후 cmd-entry.md 및 Phase 3 태스크 업데이트 필요.

---

### fix-008: r000 virtual baseline의 dangling 예외 처리

**문제:** `EntryRevRef`에서 `None → @r000`은 가상 기준점으로 실제 파일 없음.
`elf validate`의 dangling 검사가 `@r000` 참조를 "not found"로 잡을 수 있음.

**결정 필요:**
- `@r000`을 "entry 생성 시점의 가상 snapshot"으로 validate에서 명시적 예외 처리.
- `schema/validate.rs`의 dangling 검사 함수에 `is_virtual_baseline(ref: &str) -> bool` 헬퍼 추가.

> **구현 시 주의** — Phase 6 태스크에 체크리스트 항목으로 추가 필요.

---

### fix-009: attachments/ 빈 디렉터리 git 추적

**문제:** `attachments/`는 빈 디렉터리로 생성되며 git이 기본적으로 추적하지 않음.

**선택지:**
- A: `.gitkeep` 파일 자동 생성 (git 추적 보장)
- B: 아무것도 생성 안 함 (entry에 첨부 파일이 추가될 때 디렉터리가 의미를 가짐)

> **미결정**

---

### fix-010: → see 패턴을 README 템플릿에 안내

**문제:** `elf validate`가 `→ see N####` 인라인 패턴을 스캔하지만, 사용자가 이 컨벤션을 모르면 활용 불가.

**결정:** `elf init`이 생성하는 `README.md` 템플릿에 이 패턴을 명시적으로 안내.

```markdown
## 인라인 cross-reference

note.md나 revision 본문에서 다른 entry를 참조할 때:
`→ see N####` 패턴을 사용하세요. `elf validate`가 dangling 여부를 자동 검사합니다.
```

---

### fix-011: r### digit format 명시

**문제:** validate naming rule은 `r\d{3}` (3자리 고정), `RevisionId(u32)`는 상한 없음.
r1000 이상이 되면 naming rule 위반.

**선택지:**
- A: `r{n:03}` 고정 — 999개 revision까지. 개인용 vault에서 실질적 상한.
- B: 가변 자리수 `r{n}` — validate naming rule 완화 필요.

> **미결정** — 결정 후 validate naming rule 정규식 업데이트.
