Files
kebab/docs/superpowers/plans/2026-05-25-p9-fb-41-finalize-plan.md
altair823 98cf4e8a04 chore(release): bump version 0.17.2 → 0.18.0 + cut fb-41 multi-hop
v0.18.0 cut PR. fb-41 multi-hop RAG + NLI verification 의 user-visible surface (PR #176-180) + post-PR9 cleanup/refactor (PR #181) ship 마무리.

## 변경 사항

### Version
- workspace `Cargo.toml`: 0.17.2 → 0.18.0. Cargo.lock 자동 cascade (24 kebab-* crate 모두 0.18.0).

### Frozen design contract
- `docs/superpowers/specs/2026-04-27-kebab-final-form-design.md`:
  - §3.8 RAG types — RefusalReason 에 NliVerificationFailed + NliModelUnavailable + MultiHopDecomposeFailed 추가 + Multi-hop RAG + NLI verification 의 ask_multi_hop facade + step 8.5 NLI hook + HopRecord / VerificationSummary 명시.
  - §9 versioning rules 표 — nli_model_version row 신규 (선택 — v0.19+ second adapter 시 wire surface candidate).

### Status transitions
- `docs/superpowers/specs/2026-05-25-p9-fb-41-finalize-spec.md`: status approved-by-team → completed.
- `docs/superpowers/plans/2026-05-25-p9-fb-41-finalize-plan.md`: status approved-by-team → completed (spec_status 도).

### User-facing docs
- `README.md`: 명령 표의 `kebab ask` row 에 `--multi-hop` flag + NLI 옵션 안내 한 단락 (mDeBERTa-v3 XNLI 280 MB 자동 다운로드 / RAM peak ~7-8 GB / threshold tuning 0.5 prod / 0.0 disable).
- `docs/SMOKE.md`: `[rag] nli_threshold = 0.0` config 예시 + 활성화 절차 + first-run download + RAM 권장 inline 안내.

### Handoff + dashboard
- `HANDOFF.md`: 한 줄 요약 의 현재 version 0.17.2 → 0.18.0. v0.18.0 cut entry 추가 (fb-41 multi-hop + NLI + cleanup ship). Component 카운트 단락에 fb-41 PR-9 의 kebab-nli + ask_multi_hop 추가 명시. 머지 후 결정 절 맨 위에 v0.18.0 fb-41 entry 신규.
- `tasks/INDEX.md`: p9-fb-41  머지 (v0.18.0). v0.18.0 subsection 신규 — PR #176-181 의 6 sub-PR + cleanup 각 한 줄 요약.

## 비범위 / 별 작업
- HOTFIXES.md 의 fb-41 entry 는 이미 PR #180 (PR-9d closure) 에서 작성 완료 — 본 cut PR 에서 추가 anchor 불필요.
- SKILL.md 의 v0.18+ NLI 안내는 이미 PR-9c-2 에서 inline 추가 완료.

## 검증
- `cargo check --workspace -j 1` 통과 (모든 24 crate v0.18.0 확인).
- frozen design 의 RefusalReason enum 확장이 kebab-core 의 production code 와 정합 (PR-9c-1 시점부터 동일 variants 있음).

Wire 영향: 없음 (additive minor 는 PR-9c-1 에서 이미 ship, 본 commit 은 documentation cascade only).
Behavior 영향: 없음.

머지 후 `gitea-release v0.18.0` 으로 tag + release notes 작성.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 05:18:08 +00:00

37 KiB
Raw Blame History

title, date, task_id, phase, status, target_version, design, spec_review_round, spec_status, plan_review_round, plan_review_outcome
title date task_id phase status target_version design spec_review_round spec_status plan_review_round plan_review_outcome
p9-fb-41 finalize implementation plan v4 — NLI verification + v0.18.0 cut 2026-05-25 p9-fb-41-finalize P9 completed 0.18.0 ../specs/2026-05-25-p9-fb-41-finalize-spec.md 5 completed 3 All 4 OMC team reviewers APPROVE (plan v4 round 3, FINAL convergence). - architect: APPROVE (round 1 plan v2) - planner: APPROVE (round 2 spec + plan v2 re-confirmed) - document-specialist: APPROVE (round 2 plan v3 — NIT-1 minor) - critic: APPROVE (round 3 plan v4 FINAL — 5 axes 95.4% production excellence baseline)

p9-fb-41 finalize plan v4

spec: docs/superpowers/specs/2026-05-25-p9-fb-41-finalize-spec.md (review_round=5, APPROVE by all 4 OMC team reviewers).

0. 작업 개요

PR-1 ~ PR-8 머지 후 v0.18 pre-cut 도그푸딩 (/build/cache/dogfood-v018/results/SUMMARY.md) 에서 발견된 S7 hallucination 의 진짜 fix (NLI post-synthesis verification) + v0.18.0 cut.

총 5 sub-PR (9a / 9b / 9c-1 / 9c-2 / 9d) + 1 cut PR. 총 추정 시간: 작업 21-31h / wall time 28-44h (§8 cumulative trace + plan v4 round-2 critic M2 분리 참조).

PR sequence 는 순차 (각 PR 머지 후 다음 시작) — sub-PR 별 surface 가 다음 sub-PR 의 기반:

PR-9a (skeleton)
  ↓ 머지 후
PR-9b (ONNX inference)
  ↓ 머지 후
PR-9c-1 (core types + wire scaffolding)
  ↓ 머지 후
PR-9c-2 (pipeline integration + mock test)
  ↓ 머지 후
PR-9d (dogfood retest + HOTFIXES)
  ↓ 머지 후
cut PR (chore: bump version 0.17.2 → 0.18.0)
  ↓ 머지 + tag v0.18.0

본 plan 은 subagent-driven-development 의 task list — 각 sub-PR 의 self-contained description.

1. 머지된 PR-1 ~ PR-8 의 carry-over

각 PR 의 회차 리뷰 carry-over 항목은 본인 PR 안 또는 후속 PR 에서 해소됨. 본 plan 의 PR-9 sub-PRs 에는 추가 carry-over 없음 — clean baseline.

2. PR-9a — kebab-nli crate skeleton

Goal: trait surface + scaffolding + workspace dep chain 도입. implementation 없이도 build 가능.

Pre-flight (PR-9a 시작 전, manual) — spec §2.1 + §3 PR-9a:

  1. Model + tokenizer file 존재 검증:

    curl -I https://huggingface.co/Xenova/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7/resolve/main/onnx/model.onnx
    curl -I https://huggingface.co/Xenova/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7/resolve/main/tokenizer.json
    

    둘 다 200 OK 확인. 실패 시 PR-9 design re-evaluation.

  2. tokenizers features 검증 (standalone repro):

    cargo new --bin /tmp/nli-tok-probe
    cd /tmp/nli-tok-probe
    cargo add tokenizers --no-default-features -F onig
    wget https://huggingface.co/Xenova/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7/resolve/main/tokenizer.json
    # main.rs: tokenizers::Tokenizer::from_file("tokenizer.json").expect("load");
    cargo run --release
    

    성공 시 PR-9a features lock. 실패 시 default-features = true fallback. 결과 + 최종 features set 을 PR-9a PR description 의 ## Cargo features 결정 trace 절에 첨부.

Files:

  • Cargo.toml (workspace):
    • members"crates/kebab-nli" 추가.
    • workspace.dependencies 에 추가 (fastembed transitive 와 정확히 일치):
      • ort = { version = "=2.0.0-rc.9", default-features = false, features = ["ndarray"] }
      • tokenizers = { version = "0.21", default-features = false, features = ["onig"] } (pre-flight 결과에 따라 features 갱신 가능)
      • hf-hub = { version = "0.4", default-features = false, features = ["ureq", "rustls-tls"] }
      • ndarray = "0.16"
  • crates/kebab-nli/Cargo.toml (skeleton 의존만):
    • dependencies: kebab-config, anyhow, serde.
    • dev-dependencies: tempfile.
  • crates/kebab-nli/src/lib.rs:
    • NliScores struct + faithfulness() + from_xnli_logits().
    • NliVerifier trait.
    • private softmax3 helper.
  • crates/kebab-nli/src/onnx.rs:
    • OnnxNliVerifier placeholder struct.
    • OnnxNliVerifier::new(&Config) -> Result<Self> placeholder.
    • impl NliVerifier::score → bail!("PR-9a stub").

Tests (6 unit):

  • softmax3_normalises_to_unit, softmax3_is_invariant_to_constant_shift.
  • nli_scores_from_xnli_logits_orders_correctly, faithfulness_returns_entailment_channel.
  • new_succeeds_on_default_config, score_returns_err_in_skeleton.

검증:

  • cargo test -p kebab-nli -j 1 — 6 통과.
  • cargo clippy -p kebab-nli --all-targets -j 1 -- -D warnings clean.

시간: 2-3h.

3. PR-9b — OnnxNliVerifier 의 ONNX inference + model download

Goal: OnnxNliVerifier::score 의 진짜 implementation.

Dependency: PR-9a 머지 완료.

Files:

  • crates/kebab-nli/Cargo.toml:
    • ort, tokenizers, hf-hub, ndarray, tracing 추가 (workspace.dependencies).
  • crates/kebab-nli/src/onnx.rs:
    • OnnxNliVerifier fields: model_id, cache_dir (= config.storage.model_dir.join("nli").join(sanitize(model_id))), session: OnceLock<ort::Session>, tokenizer: OnceLock<tokenizers::Tokenizer>.
    • OnnxNliVerifier::new(&Config) -> Result<Self> — model_id / cache_dir stamp + lazy load deferred.
    • ensure_loaded(&self) -> Result<(&Session, &Tokenizer)> — hf-hub download + Tokenizer::from_file + Session::commit_from_file + truncation params 설정.
    • score(premise, hypothesis) — encode pair (with OnlyFirst truncation) → ort run → softmax → NliScores.
    • sanitize_model_id(s: &str) -> String helper.
  • crates/kebab-nli/tests/inference.rs 신규:
    • #[ignore] integration tests (5 cases):
      1. EN entailment ("Caffeine is a stimulant.""Caffeine is a stimulant.") — entailment > 0.8.
      2. EN no-entailment (caffeine → C8H10N4O2) — entailment < 0.3.
      3. KR entailment ("사과는 빨갛다.""사과는 색이 있다.") — entailment 높음.
      4. Long premise (10000 char) → truncation 적용 + 정상 score (panic 없음).
      5. Empty hypothesis → graceful error.

Manual smoke protocol (PR description 강제 첨부):

cargo test -p kebab-nli -j 1 --test inference -- --ignored 2>&1 | tail -20
  • 5 test 모두 PASS 확인.
  • case 1 의 NliScores dump (예: entailment=0.92, neutral=0.05, contradiction=0.03) 를 PR body 의 ## 검증 절에 inline.

검증:

  • unit test 통과 + clippy clean.
  • --ignored integration test 의 manual run (PR 작업자 책임).

시간: 8-12h (round-2 planner P2 갱신).

Risks:

  • ort 2.0-rc.9 API stability — workspace pin "=2.0.0-rc.9" (fastembed transitive 일치).
  • mDeBERTa ONNX 존재 — PR-9a pre-flight 가 검증.
  • tokenizers SentencePiece 호환성 — PR-9a pre-flight 가 검증.
  • hf-hub ureq + rustls-tls vs fastembed native-tls features union — PR-9a 의 첫 build 가 검증.

4. PR-9c-1 — Core types + wire scaffolding

Goal: RefusalReason + VerificationSummary + RagPipeline.verifier field + Config + wire schema.

Dependency: PR-9b 머지 완료.

Files:

  • crates/kebab-core/src/answer.rs:
    • RefusalReason::NliVerificationFailed + RefusalReason::NliModelUnavailable 신규.
    • Answer.verification: Option<VerificationSummary> field.
    • VerificationSummary { nli_score: f32, nli_threshold: f32, nli_passed: bool } 신규 struct.
  • crates/kebab-config/src/lib.rs:
    • NliCfg 신규 struct + [models.nli]:
      • model: String (default "Xenova/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7").
      • provider: String (default "onnx").
    • RagCfg.nli_threshold: f32 (default 0.0 — disabled).
    • env override: KEBAB_MODELS_NLI_MODEL, KEBAB_RAG_NLI_THRESHOLD.
  • crates/kebab-rag/src/pipeline.rs:
    • RagPipeline 의 새 field: verifier: Option<Arc<dyn NliVerifier>> (None = verify off).
    • 시그니처 widening = Option B (builder): 기존 RagPipeline::new(config, retriever, llm, sqlite) 시그니처 유지 + 신규 pub fn with_verifier(self, v: Arc<dyn NliVerifier>) -> Self builder.
    • kebab-rag 의 Cargo.toml 에 kebab-nli 의존 추가.
    • #[allow(dead_code)] 처리 (round-2 critic M1 closure): PR-9c-1 의 verifier field 와 with_verifier builder 는 PR-9c-2 의 ask_multi_hop step 8.5 hook 가 활성화될 때까지 unused — cargo clippy -- -D warningsdead_code lint fail risk. PR-9c-1 의 verifier field 에 #[allow(dead_code)] 임시 attribute (Cargo.toml 의 kebab-nli 의존 자체는 active path). 또는 placeholder smoke test (pipeline.with_verifier(MockVerifier::default()).verifier.is_some() 한 줄). PR-9c-2 가 hook 추가 시 attribute 제거.
  • docs/wire-schema/v1/answer.schema.json:
    • verification field 추가 (anyOf [object, null]) + $defs.VerificationSummary 인라인:
      "VerificationSummary": {
        "type": "object",
        "required": ["nli_score", "nli_threshold", "nli_passed"],
        "properties": {
          "nli_score":     { "type": "number" },
          "nli_threshold": { "type": "number" },
          "nli_passed":    { "type": "boolean" }
        }
      }
      
    • refusal_reason.enum"nli_verification_failed", "nli_model_unavailable" 추가.
  • docs/wire-schema/v1/error.schema.json:
    • code enum 에 nli_verification_failed, nli_model_unavailable 추가.
    • details.description 에 두 항목 추가 (multi_hop_decompose_failed 패턴):
      • nli_verification_failed: { score, threshold } (reserved — currently emitted as Answer.refusal_reason on stdout, NOT as error.v1; forward-looking for future RefusalReason → error_wire promotion).
      • nli_model_unavailable: { source } (reserved — same pattern as nli_verification_failed).
  • docs/ARCHITECTURE.md (round-1 document-specialist ISSUE-1 — CLAUDE.md "A new crate is added — extend the graph + directory tree" rule):
    • Mermaid Adapters subgraph 에 nli["kebab-nli<br/>(NLI verifier)"] 노드 추가.
    • Edges (round-2 critic R2-NIT-3 — forward-looking final state 명시):
      • PR-9c-1 시점 직접 의존 추가 = rag --> nli (kebab-rag/Cargo.toml kebab-nli 추가) + nli --> config (kebab-nli/Cargo.toml kebab-config 추가).
      • app --> nli edge 는 forward-looking (PR-9c-2 에서 kebab-app/Cargo.toml 의 kebab-nli 의존 추가됨) — PR-9c-1 의 ARCHITECTURE.md 가 최종 graph 상태 반영 결정 (single update, 9c-2 에서 재변경 회피). 결정 trade-off: forward-looking 가 reader 의 final state 가시 ↑, current state 가 PR-9c-1 시점 정확도 ↑. 권장 forward-looking (graph 가 surface 명세, 한 번 갱신).
      • nli --> core edge 는 PR-9a 머지 후 crates/kebab-nli/Cargo.toml 의 final [dependencies] 확인 결정 (round-2 document-specialist NIT-1) — kebab-core 직접 의존 시 edge 포함, config 경유 transitive 만이면 edge 생략. ARCHITECTURE.md graph 관례 = 직접 Cargo.toml 의존 기준.
    • 디렉토리 트리에 crates/kebab-nli/ 항목 추가.

Tests:

  • crates/kebab-config/src/lib.rs::tests:
    • default_nli_threshold_is_zero.
    • default_nli_model_is_xenova_mdeberta.
    • legacy_config_without_nli_uses_defaults.
    • env_override_nli_threshold.
  • crates/kebab-cli/tests/wire_ask_multi_hop.rs:
    • answer_schema_declares_verification_field_and_defs.
    • answer_schema_refusal_reason_enum_includes_nli_verification_failed.
    • answer_schema_refusal_reason_enum_includes_nli_model_unavailable.
    • error_schema_code_enum_includes_nli_verification_failed.
    • error_schema_code_enum_includes_nli_model_unavailable.

검증:

  • cargo test --workspace -j 1 — 회귀 0 (기존 multi-hop tests pass, RagPipeline::new 시그니처 unchanged).
  • cargo clippy --workspace --all-targets -j 1 -- -D warnings clean.

시간: 2-3h.

5. PR-9c-2 — Pipeline integration + mock test

Goal: ask_multi_hop 의 NLI verify wiring + mock test + SKILL.md 갱신.

Dependency: PR-9c-1 머지 완료.

Files:

  • crates/kebab-rag/src/pipeline.rs:
    • ask_multi_hop 의 step 8.5 NLI hook (spec §2.3 코드):
      • empty answer guard: if !acc.trim().is_empty() { /* step 8.5 */ }.
      • if self.config.rag.nli_threshold > 0.0 { /* verify */ } outer guard.
      • inner verify: truncate_for_nliverifier.score → score 검사 → refuse 또는 진행.
    • refuse_nli_verification helper (refuse_* 패턴) — verification: Some(...) 채움.
    • refuse_nli_model_unavailable helper — verification: None.
    • pub fn truncate_for_nli(premise: &str, hypothesis: &str) -> (String, bool) helper:
      • max premise char count = MAX_NLI_PREMISE_CHARS = 4 * 400 ≈ 1600 chars.
      • hypothesis 길이 + special tokens 32 char budget 적용 후 자연 보존.
      • 둘째 return = was_truncated boolean.
      • token ratio 가정: 4 char ≈ 1 token (영어 BPE). 한국어 SentencePiece 는 1-2 char/token — tokenizer OnlyFirst backup. v0.18.1 의 token-count 기반 budget 갱신 candidate.
  • crates/kebab-app:
    • 실제 constructor 이름 = App::open_with_config (round-2 critic R2-NIT-4 verification — crates/kebab-app/src/app.rs:187. spec §3 PR-9c-2 의 App::new논리적 이름 — 실제 code 의 함수명으로 mapping). 시그니처 이미 Result<Self, anyhow::Error> (현재 line 187 pub fn open_with_config(config: kebab_config::Config) -> Result<Self>) — caller cascading 없음 (kebab-cli/tui/mcp 의 App::open_with_config(...) 호출 site 의 ? 또는 .context(...) 그대로). round-2 NEW-M2 의 시그니처 widening = body 추가만 (OnnxNliVerifier::new(config)? integration).
    • config.rag.nli_threshold > 0.0OnnxNliVerifier::new(config)? 호출 + Arc::new wrap + pipeline.with_verifier(v).
    • config.rag.nli_threshold == 0.0 → verifier = None, 기존 path.
    • OnnxNliVerifier::new 실패 시 bail!() — user-facing crash 회피.
  • crates/kebab-rag/tests/multi_hop.rs:
    • common/mod.rsMockNliVerifier { scores: NliScores } helper.
    • multi_hop_nli_pass_keeps_grounded — entailment 0.9 → grounded=true, verification.nli_passed=true.
    • multi_hop_nli_fail_refuses — entailment 0.1 → refusal=NliVerificationFailed.
    • multi_hop_nli_disabled_skip_verify — threshold = 0.0 → verify skip, verification=None.
    • multi_hop_nli_model_unavailable_refuses — verifier Err → refusal=NliModelUnavailable.
    • multi_hop_truncate_for_nli_preserves_hypothesis — long premise + 짧은 hypothesis → hypothesis 그대로.
  • integrations/claude-code/kebab/SKILL.md:
    • mcp__kebab__ask 절에 NLI 안내 한 줄:

      answer.v1.verification.nli_passed 의미 (true = NLI 통과, false = refusal_reason = nli_verification_failed). threshold tuning 권장 (0.5 production, 0.9 strict). nli_model_unavailable refusal 시 user 의 [rag] nli_threshold = 0.0 임시 disable + network/disk 복구 후 재시도.

Tests: 5 신규 multi-hop tests + 기존 tests 회귀 0.

검증:

  • cargo test --workspace -j 1 — 모든 test 통과 + 신규 5 multi-hop pass.
  • cargo clippy --workspace --all-targets -j 1 -- -D warnings clean.

시간: 3-4h.

6. PR-9d — Dogfood retest + HOTFIXES closure

Goal: PR-9c 머지 후 dogfood corpus 에서 S7 + S1 + S3 + S10 retest.

Dependency: PR-9c-2 머지 완료.

Pre-run prereq (manual + subagent 양쪽 적용) — spec §3 PR-9d:

  • Ollama service running (curl -s 127.0.0.1:11434/api/tags).
  • dogfood corpus 디렉토리 존재 (/build/cache/dogfood-v018/queries/*.txt).
  • network reachable (hf-hub 280 MB NLI model first-run download 가능).
  • free RAM ≥ 6 GB.
  • release binary path: /build/out/cargo-target/release/kebab (CARGO_TARGET_DIR) 또는 ./target/release/kebab (in-tree). 권장: /build/out/cargo-target/release/kebab (HOTFIXES 2026-05-25 fb-41 dogfood entry).

prereq 실패 시 조기 abort + 사용자 보고. partial dogfood 결과 commit 회피.

Tests (자동화 없음, manual run):

  • [rag] nli_threshold = 0.5 config (production 권장).
  • S7 / S1 / S3 / S10 multi-hop ask → 각각 NLI score 측정 + grounded/refuse 확인.
  • single-pass S7 (verification 없음) baseline 도 같이 측정.

RAM peak protocol:

# 시작 전 baseline
ps -o rss=,vsz=,comm= -p $(pgrep -f 'ollama|kebab')

# multi-hop ask 진행 중 1초 sampling (5분 cap)
while sleep 1; do ps -o rss=,comm= -p $(pgrep -f 'ollama|kebab') ; done > /tmp/ram-S<N>.log &
RAM_PID=$!

# kebab ask 실행
/build/out/cargo-target/release/kebab ask --multi-hop "<query>" --json

# sampling 종료 + peak 추출
kill $RAM_PID
awk '{sum+=$1} END {print sum/NR " avg KB"}' /tmp/ram-S<N>.log
awk '{ if ($1>max) max=$1 } END { print max " peak KB" }' /tmp/ram-S<N>.log

peak < 10 GB (16 GB 환경 OOM 없음) 확인.

Files:

  • tasks/HOTFIXES.md:
    • "PR-9 closure (post-v0.18 dogfood retest)" sub-section 추가 — pre/post 결과 비교 표.
  • docs/dogfood/v0.18.0/ 신규 디렉토리 (round-2 P5 의 보존 path):
    • SUMMARY.md — sanitized dogfood 보고서 (원본 /build/cache/dogfood-v018/results/SUMMARY.md 의 repo 포함 가능 부분).
    • s7-multihop-post-pr9.json — S7 multi-hop NLI 결과 sample (refuse + nli_score).
    • s1-multihop-post-pr9.json — S1 multi-hop NLI 결과 sample (grounded + nli_score).
  • /build/cache/dogfood-v018/results/post-pr9/ (작업 디렉토리, repo 외):
    • 시나리오별 JSON dump + findings.md + RAM log.

검증 — spec §7 PASS criteria 표 따름:

  • S7: grounded=false, refusal=nli_verification_failed, nli_score < 0.3.
  • S1: grounded=true, refusal=None, nli_score ≥ 0.6.
  • S3 (EN): primary grounded=true 또는 acceptable degraded LlmSelfJudge.
  • S10 (KR): primary refusal=nli_verification_failed 또는 acceptable degraded LlmSelfJudge.
  • range 밖 시 threshold / model 재검토 (spec §6 iteration trigger).

시간: 4-6h (RAM 측정 + corpus 보존 + HOTFIXES + manual retest).

Scope: PR default. 작업자 선택 가능 (별 commit 가능, round-1 P3).

7. v0.18.0 cut PR (PR-9d 머지 후)

Goal: version bump + cascading docs + frozen design contract 갱신 + release tag.

Dependency: PR-9d 머지 완료.

Same-commit / Same-PR (CLAUDE.md "Release / binary version bump" rule):

  • Cargo.toml version bump + tag = 같은 commit.
  • frozen design §3.8 갱신 = 본 cut PR 안.
  • gitea-release tag v0.18.0 = 본 PR 머지 commit 위 즉시.

Merge strategy (round-1 critic P5-NEW-M2): kebab 의 default merge commit 패턴은 Merge pull request '...' (#N) 형태 — bump commit 이 PR branch 안에 있고 main 의 HEAD = merge commit (별 SHA). CLAUDE.md "bump commit = release commit" rule strict 해석:

  • Option A 권장 — gitea-pr 의 squash merge 사용 (gitea-pr --merge-method squash 또는 머지 UI 의 squash 옵션). 결과: main HEAD = bump 의 squash commit (single SHA). gitea-release v0.18.0 tag 가 그 commit 위.
  • Option B (대안): bump 의 PR branch commit 에 직접 tag (main 의 merge commit 과 다른 SHA, 그러나 release tag 는 PR branch SHA reference — gitea 에서 가능). audit trail 약간 약함.
  • Option C: merge commit 자체에 bump 내용 포함 (PR description = bump + cascading docs). gitea-pr 의 default merge commit message 가 bump 의 commit message 와 다른 자체 message — bump 의 의도 가 merge commit 에 inline 되지 않음. 권장 안 함.

본 cut PR 작업자가 Option A (squash merge) 채택. main HEAD = bump commit, tag = same SHA. CLAUDE.md rule strict 정합.

R5-NEW-NIT-1 carry-over (round-1 critic P5-NEW-M1): release notes draft (spec §5 line 681) 의 9B+ 모델 표현이 spec §5 step 8 line 651 의 8B+ Q4 모델 (gemma4:e4b 8B / gemma2:9b 등) 와 inconsistency 잔존 (cut PR 시점 final 작성 시 정정). cut PR 작업자가 spec §5 step 8 wording 일관 적용. spec round-5 NIT 자체는 spec 안에서 closure (R5-NEW-NIT-1 row of §9), 본 plan §7 가 implementation reminder.

Files (모두 한 PR, commit msg chore(release): bump version 0.17.2 → 0.18.0 + cut fb-41 multi-hop — round-2 critic R2-NIT-2 scope label):

  1. Cargo.toml (workspace): version = "0.17.2""0.18.0". Cargo.lock 자동 cascade.
  2. HANDOFF.md:
    • 한 줄 요약 (P0~P9 + P10 + v0.18.0 fb-41 multi-hop ship).
    • 머지 후 결정 절에 fb-41 entry 단락 (PR-1~PR-9 + dogfood + NLI 한 문단).
  3. tasks/HOTFIXES.md: 기존 fb-41 entry 들 post-v0.18 anchor.
  4. tasks/INDEX.md: fb-41 status opencompleted. v0.18.0 release subheader.
  5. docs/superpowers/specs/2026-04-27-kebab-final-form-design.md:
    • §3.8 RAG 의 multi-hop sub-section 추가 (본 finalize spec §1-§3 요약 verbatim).
    • §9 versioning cascade 표에 (선택) nli_model_version row.
  6. docs/superpowers/specs/2026-05-25-p9-fb-41-finalize-spec.md:
    • status: approved-by-teamcompleted.
  7. docs/superpowers/plans/2026-05-25-p9-fb-41-finalize-plan.md:
    • status: opencompleted.
  8. integrations/claude-code/kebab/SKILL.md:
    • v0.18.0 release notes link 한 줄.
  9. README.md:
    • kebab ask --multi-hop + NLI 옵션 안내 한 단락 (model first-run download cost, RAM 권장).
    • binary path confusion 한 줄 (/build/out/cargo-target/release/kebab 명시).
  10. docs/SMOKE.md:
    • NLI 옵션 활성화 절차 ([rag] nli_threshold = 0.5).
    • first-run model download 안내 (~280 MB).
    • RAM 권장 (gemma3:4b 기준 ~5-6 GB; 8B+ Q4 모델 추정 ~10 GB / 16 GB 경계).

gitea-release (cut PR 작업자 결정):

# Option A: --auto-notes 만 (gitea-ops skill 가 PR 시리즈 자동 list 생성)
gitea-release v0.18.0 --auto-notes

# Option B: --notes 만 (spec §5 release notes draft inline)
gitea-release v0.18.0 --notes-file release-notes-v0.18.0.md

# Option C: 둘 다 (gitea-ops 가 동시 명시 시 동작 — 사전 확인 필요)
gitea-release v0.18.0 --auto-notes --notes "fb-41 multi-hop RAG + NLI ship..."

cut PR 작업자가 gitea-release --help 또는 ~/.claude/skills/gitea-ops/SKILL.md 확인 후 Option 선택 (round-1 critic P5-NIT-1). spec §5 의 release notes draft 가 content — 어느 path 로 input 할지가 결정.

권장: Option A (--auto-notes) + PR description 에 spec §5 release notes draft inline — gitea-release 가 PR description 의 release notes 절을 carry. 단순 + audit trail.

검증:

  • cargo build --release 통과.
  • gitea-pr-status gate passed.
  • 머지 후 binary smoke test (cargo run --release).

시간: 2-3h (round-1 architect N4 + critic P5-NIT-4 반영 — frozen design §3.8 verbatim + 10 files cascading + release notes final + review iteration 가능성 반영).

8. 시간 추정 합산

Sub-PR 작업 (h) + review iteration (h) wall time (h)
PR-9a 2-3 1-2 3-5
PR-9b 8-12 2-3 10-15
PR-9c-1 2-3 1-2 3-5
PR-9c-2 3-4 1-2 4-6
PR-9d 4-6 1-2 5-8
cut PR 2-3 1-2 3-5
Total 21-31h +7-13h 28-44h

round-2 plan critic M2 closure — 작업 시간 vs wall time 명시적 분리:

  • 작업 (h) = 순수 implementation / dogfood / docs cascade 시간 (review feedback 반영 작업 별도).
  • review iteration (h) = gitea-pr-review 회차당 1-2h × 평균 1-1.5 회차 추정 (HOTFIXES 평균 의거). 회차 0 (즉시 APPROVE) 도 가능 — 작업자 quality 의존.
  • wall time (h) = 작업 + review iteration. 사용자 / stakeholder 의 ship expectation baseline.

cumulative 정정 trace: round-1 14-20h → round-2 14-22h → plan v2 20-30h → plan v3 21-31h (작업) / 28-44h (wall time, plan v4 round-2 critic M2 신설).

9. /subagent-driven-development 의 task list

plan 통과 + OMC team APPROVE 후 다음 task list 로 subagent dispatch:

  1. Task PR-9a: kebab-nli crate skeleton — plan §2 + spec §3 PR-9a + spec §2.1~§2.2.4 참조. branch feat/fb-41-pr-9a-kebab-nli-crate. pre-flight (curl -I + tokenizers probe) 결과 PR description 첨부. 검증 + PR + 회차 리뷰 루프 + 머지.
  2. Task PR-9b: OnnxNliVerifier inference — plan §3 + spec §3 PR-9b + spec §2.2.2~§2.2.4 참조. branch feat/fb-41-pr-9b-onnx-nli-inference. manual --ignored smoke 결과 PR description 첨부.
  3. Task PR-9c-1: core types + wire — plan §4 + spec §3 PR-9c-1 + spec §2.4~§2.6 참조. branch feat/fb-41-pr-9c-1-core-types-wire. docs/ARCHITECTURE.md 갱신 포함.
  4. Task PR-9c-2: pipeline integration + mock test + SKILL.md — plan §5 + spec §3 PR-9c-2 + spec §2.3 참조. branch feat/fb-41-pr-9c-2-pipeline-integration.
  5. Task PR-9d: dogfood retest + HOTFIXES + dogfood corpus 보존 — plan §6 + spec §3 PR-9d + spec §7 PASS criteria 표 참조. branch feat/fb-41-pr-9d-dogfood-retest. pre-run prereq 검증 후 시작. Environment (round-2 critic M3): user machine 에서만 dispatch 가능 — Ollama service running + dogfood corpus 디렉토리 존재 + network reachable + free RAM ≥ 6 GB + release binary path 의존. isolated docker / ephemeral CI container 환경은 모두 부재 → dispatch 시 즉시 abort. autonomous subagent provisioning (sudo Ollama install + corpus mirror) 은 v0.19+ candidate.
  6. Task cut PR: version bump + cascading docs — plan §7 + spec §5 + spec R5-NEW-NIT-1 carry-over 참조. branch chore/v0.18.0-cut. gitea-pr squash merge + gitea-release v0.18.0 tag 머지 commit 위.

각 task 는 self-contained — 별 subagent dispatch 가능. dependency 는 이전 task 의 main 머지 — subagent 가 다음 task 시작 전 git pull 로 sync. 순차 only — speculative pre-work 권장 안 함 (review 부담 + rebase 위험). 특히 PR-9c-2 는 PR-9c-1 의 review iteration 완료 + 머지 후 시작 (round-2 critic N4) — 중간 schema change 시 9c-2 의 mock test 의 schema validation expectation 변경 위험. TaskUpdateaddBlockedBy chain 으로 race 회피 (round-1 planner informational). active subagent ≤ 1 임의 timestamp — RAM 압박 회피 + user memory feedback_serial_build_only policy 정합.

각 subagent 는 다음을 책임:

  • branch 생성 + 구현 + tests + cargo test/clippy 검증 (16 GB RAM 직렬 only, user memory feedback_serial_build_only 적용).
  • gitea-pr 생성 + 리뷰 루프 (gitea-pr-review 회차) + APPROVE 후 머지.
  • 머지 후 main checkout + pull + branch cleanup (git branch -d + worktree 사용 시 git worktree remove).
  • cargo clean 권장 (CLAUDE.md routinely after merged PR rule, 92GB→0GB 복구).
  • TaskUpdate(status='completed') 호출 + team-lead 에게 SendMessage 으로 다음 task 시작 신호 (또는 사용자 manual dispatch).

10. Self-review notes

  • PR-9 의 ONNX integration새 dep chain (ort + tokenizers + hf-hub) 도입 — 첫 사용 안정화 필요. PR-9a 의 pre-flight 가 모든 위험 검증. PR-9b 의 #[ignore] test manual smoke 가 production binary 실제 동작 검증.
  • multi-hop NLI 의 latency 추가 — current multi-hop synthesize 158s + NLI ~50ms ≈ 158s. negligible.
  • Model first-run download (~280 MB) — 사용자 도그푸딩 환경 (CPU only) 의 disk + download bandwidth 1회 비용. README + SMOKE 안내. fail-closed download failure 정책.
  • RagPipeline::new 시그니처 widening — Option B (builder) 결정 — 18+ existing call sites 무영향.
  • frozen design contract §3.8 갱신 timing — v0.18.0 cut PR 안 — PR-9c 가 contract 변경 안 함.
  • kebab-nli 의 trait + impl 동일 crate — v0.18 scope = adapter 1개. v0.19+ 에 multi-adapter 등장 시 kebab-nli-onnx 분리.
  • dogfood corpus 보존docs/dogfood/v0.18.0/ 신규 dir + sanitized SUMMARY + sample JSON.
  • RAM cold-start 측정 — PR-9d 의 PASS criteria 에 포함, release notes 의 권장 RAM 한 줄.
  • ort version pinworkspace.dependencies.ort = "=2.0.0-rc.9".

Plan-specific self-review (round-1 critic P5-NIT-3 반영)

execution / coordination 측면의 추가 self-review notes:

  • Subagent 간 race 회피: TaskUpdate.addBlockedBy chain 필수 적용 — PR-9b 의 task 가 PR-9a task 의 머지 완료에 blockedBy. PR-9c-1 → 9c-2 → 9d → cut PR 동일 chain.
  • PR-9c-1 wire schema baseline for 9c-2: PR-9c-1 의 answer.schema.json / error.schema.json 변경이 9c-2 의 mock test 의 schema validation baseline. PR-9c-1 의 review iteration 결과 schema 변경 시 9c-2 시작 전 main pull + spec/plan re-check 필수.
  • #[allow(dead_code)] for verifier field in PR-9c-1 (round-1 architect N1): PR-9c-1 의 RagPipeline.verifier field 가 declared 되었지만 read by nothing 인 interim 시기 (9c-2 머지 전) — cargo clippy -- -D warnings fail 위험. PR-9c-1 의 field 에 임시 #[allow(dead_code)] 또는 Debug derive 의 trivial field access. PR-9c-2 가 attribute 제거 + builder 의 with_verifier 의 사용 path 활성화.
  • OnnxNliVerifier::new 의 lazy stamp semantics (round-1 architect N2): spec §2.2.2 의 OnceLock pattern — new() 자체는 cache_dir create 같은 early error 만 잡음. download / inference 실패는 runtime pathrefuse_nli_model_unavailable 가 처리. 작업자가 eager download 시도 (lazy 위반) 회피.
  • truncate_for_nli placement (round-1 architect N3): module-level pub fn in kebab_rag::pipeline. 회귀 핀 test = crates/kebab-rag/tests/multi_hop.rsmulti_hop_truncate_for_nli_preserves_hypothesis (§5).
  • First-run download progress indicator 검증 (round-1 architect N5): PR-9d 의 first-run NLI model download 시 stderr 에 kebab-nli: downloading model.onnx (280 MB)... progress emit 확인. non---json mode 만 progress emit. PR-9d 의 검증 절에 명시 안 됐지만 작업자가 stderr 출력 확인 + HOTFIXES PR-9 closure 절에 progress 확인 결과 한 줄 명시 권장.
  • Parallel execution opportunity (round-1 critic Open Question): PR-9b (8-12h, kebab-nli crate-internal) 동안 PR-9c-1 의 kebab-nli 의존 없는 부분 (RefusalReason variant + wire schema) preparation 가능 — 시간 단축 4-6h. 권장 안 함 (review iteration 비용 ↑ + branch rebase risk). plan v3 는 sequential only 명시. 단, 작업자가 speculative pre-work 결정 시 kebab-ragkebab-nli 의존 추가는 9b 머지 후 lock.
  • PR-9d binary path 일관성 (round-1 critic Open Question + dogfood SUMMARY §부수 발견 closure): subagent task 가 cargo build --release/build/out/cargo-target/release/kebab 사용 (CARGO_TARGET_DIR env 설정 환경). cut PR 의 README 갱신 (§7 step 9) 가 user-facing path confusion closure.
  • Rollback path (round-1 critic What's missing): PR-9d dogfood retest PASS criteria catastrophic fail (NLI library bug 등) 시 PR-9 revert path — git revert PR-9c-2 → PR-9c-1 → PR-9b → PR-9a 의 reverse sequential. [rag] nli_threshold = 0.0 config knob 으로 graceful disable 가 더 가벼운 first-response. spec §6 의 threshold iteration trigger 와 분리 (혼동 회피).

11. Spec-driven 변경 trace

본 plan v4 는 spec v5 (review_round=5, approved-by-team) 의 모든 결정 반영 + plan-review round-1/round-2 의 issues closure. spec 의 §9 closure matrix (round 1-4) + plan v1/v2/v3 의 점진적 갱신 baseline. plan v2 → v4 갱신 사항 (round-2 critic R2-NIT-1 wording 정정):

  • PR-9c 분할 = 9c-1 + 9c-2 별 PR (round-2 P1).
  • 시간 추정 14-20h → 20-30h cumulative 정정 (round-2 P2 + round-3 R3-NEW-N1).
  • PR-9d 의 RAM 측정 protocol + pre-run prereq + dogfood corpus 보존 (round-2 P5/P6 + round-3 R3-NEW-N3).
  • cut PR step 명시 + same-commit rule (round-2 M7 + round-3 R3-NEW-N2).
  • 시그니처 widening = Option B (round-2 NEW-M2).
  • truncate_for_nli signature (String, bool) (round-2 NEW-N1).
  • RefusalReason::NliVerificationFailed + NliModelUnavailable wire 통일 (round-2 ISSUE-1 + R3-NEW-N3).
  • model ID Xenova/... config default 확정 (round-1 A1 / D5).
  • nli_threshold = 0.0 single gate (round-1 A3 / D3).
  • pre-flight tokenizers features 검증 (round-2 NEW-M1).
  • §7 cross-ref single source of truth (round-4 R4-NEW-M1 + R4-NEW-N1).

Plan-review round-1 closure (post-spec-v5 plan-level review)

reviewer round-1 plan issue plan v3 resolution
document-specialist ISSUE-1 ARCHITECTURE.md missing §4 PR-9c-1 Files 에 docs/ARCHITECTURE.md 추가 — Mermaid nli 노드 + 4 edges + 디렉토리 트리.
critic P5-NEW-M1 R5-NEW-NIT-1 carry-over §7 cut PR 에 "R5-NEW-NIT-1 carry-over" 절 — release notes draft 의 9B+ 모델8B+ Q4 모델 cut PR 작업자 reminder.
critic P5-NEW-M2 merge strategy §7 — Option A (gitea-pr squash merge) 권장 명시. bump commit = main HEAD = release tag SHA. CLAUDE.md same-commit rule strict 정합.
critic P5-NIT-1 gitea-release flag combo §7 — --auto-notes / --notes-file / 둘 다 의 Option A/B/C 명시 + 사전 gitea-release --help 확인 + Option A 권장.
critic P5-NIT-2 §9 spec § reference §9 — 각 task description 에 plan §X + spec §Y cross-ref inline.
critic P5-NIT-3 plan-specific self-review §10 — Plan-specific self-review notes 절 추가 (race avoidance + dead_code attr + lazy semantics + parallel opportunity + rollback path 등 8 항목).
critic P5-NIT-4 cut PR 시간 §7 + §8 — 시간 1-2h → 2-3h. 합산 20-30h → 21-31h.
architect N1 dead_code attr §10 plan-specific self-review notes 의 #[allow(dead_code)] 한 줄 명시.
architect N2 OnnxNliVerifier::new lazy semantics §10 — early error vs runtime error 명시.
architect N3 truncate_for_nli placement §10 — module-level pub fn + test 위치 cross-ref.
architect N4 cut PR 시간 (frozen design §3.8 cost) §7 시간 1-2h → 2-3h + §8 합산 정정 (critic P5-NIT-4 와 동일 fix).
architect N5 download progress 검증 §10 self-review — PR-9d 작업자가 stderr progress 확인 + HOTFIXES 한 줄 명시 권장.
planner round-2 nit (4개) spec v5 와 plan v2 가 closure (round-2 closure matrix). round-1 plan-level 신규 nit 0개 (re-confirm APPROVE).

Plan-review round-2 closure (post-plan-v3 deep ADVERSARIAL review)

reviewer round-2 plan issue severity plan v4 resolution
critic M1 PR-9c-1 dead code clippy fail risk MINOR §4 PR-9c-1 의 RagPipeline.verifier field 절에 #[allow(dead_code)] 명시 + PR-9c-2 hook 추가 시 제거 trace.
critic M2 시간 추정 review iteration 미포함 MINOR §8 시간 표 작업 vs review iteration vs wall time 분리 — 합산 21-31h (작업) / 28-44h (wall time).
critic M3 PR-9d subagent dispatch environment MINOR §9 PR-9d 항목 Environment 명시 — user machine only (Ollama / corpus / network / RAM / binary path 의존), isolated container 부적합.
critic N4 9c-1/9c-2 sequential strictness NIT §9 — "PR-9c-2 는 PR-9c-1 의 review iteration 완료 + 머지 후 시작" 명시.
document-specialist NIT-1 nli→core edge 확인 권장 NIT §4 ARCHITECTURE.md 절 — nli --> core edge 는 PR-9a 머지 후 final kebab-nli/Cargo.toml deps 확인 결정.
critic N1 spec §2.1.1 alternative models cross-ref NIT (옵션) 미반영 — PR-9d dogfood retest iteration trigger 발동 시 작업자가 spec 직접 cross-ref 권장.
critic N2 PR-9b fallback Optimum self-export NIT (옵션) 미반영 — spec §3 PR-9b 의 fallback 명시 충분.
critic N3 PR description template NIT (옵션) 미반영 — 각 plan 절이 de facto template 역할.

Plan-review round-2 (light pass on plan v3) closure

plan v3 SendMessage 후 critic round-2 light pass 가 1 actionable MINOR (R2-NEW-M1 §0 vs §8 시간 mismatch) + 1 verification MINOR (R2-NEW-M4 spec status — invalid finding: spec frontmatter 실제 status: approved-by-team) + 4 NIT (R2-NIT-1~4) 발견.

reviewer round-2 light issue severity plan v4 resolution
critic R2-NEW-M1 §0 vs §8 시간 cumulative mismatch MINOR §0 line 22 "총 추정 시간 19-28h" → "작업 21-31h / wall time 28-44h (§8 cumulative trace + plan v4 round-2 critic M2 분리 참조)".
critic R2-NEW-M4 spec status transition MINOR (invalid) spec frontmatter 확인 — status: approved-by-team (line 6). plan §7 step 6 approved-by-team → completed transition 정확. no edit.
critic R2-NIT-1 "본 plan v2" wording NIT §11 의 plan-version 헤더 — "본 plan v2 는" → "본 plan v4 는 ... + plan-review round-1/round-2 의 issues closure".
critic R2-NIT-2 cut PR commit msg scope NIT §7 Files header — chore:chore(release): scope label.
critic R2-NIT-3 ARCHITECTURE.md app --> nli edge timing NIT §4 ARCHITECTURE.md 절 — forward-looking final state 명시 + PR-9c-1 시점 직접 의존 (rag --> nli + nli --> config) 분리 + 권장 forward-looking.
critic R2-NIT-4 App::new caller cascading NIT §5 PR-9c-2 의 kebab-app 항목 — 실제 constructor App::open_with_config (kebab-app/src/app.rs:187, 이미 Result return). PR-9c-2 = body 추가만, caller cascading 0.