feat(rag): fb-41 PR-9c-1 — core types + wire scaffolding (NLI verification) #178

Merged
altair823 merged 1 commits from feat/fb-41-pr-9c-1-core-types-wire into main 2026-05-26 00:09:06 +00:00
Owner

요약

PR-9 의 wire + core 표면. behavior wiring 없음 — ask_multi_hop 의 NLI hook 은 PR-9c-2 가 활성화. 본 PR 머지 후 사용자가 [rag] nli_threshold = 0.5 를 config 에 설정 가능하지만 실 동작은 PR-9c-2 까지 dormant.

설계: docs/superpowers/specs/2026-05-25-p9-fb-41-finalize-spec.md (§3 PR-9c-1, §2.4~§2.6)
계획: docs/superpowers/plans/2026-05-25-p9-fb-41-finalize-plan.md (§4)

변경 사항

kebab-core

  • RefusalReason::NliVerificationFailed + NliModelUnavailable 추가 — serde(rename_all="snake_case") 가 wire string 자동 매핑. answer.v1.refusal_reasonerror.v1.code 동일 "nli_verification_failed" / "nli_model_unavailable".
  • Answer.verification: Option<VerificationSummary> field 추가 (#[serde(default, skip_serializing_if = "Option::is_none")] — additive minor wire).
  • VerificationSummary { nli_score: f32, nli_threshold: f32, nli_passed: bool } 신규 struct.

kebab-config

  • NliCfg { model, provider } 신규 + ModelsCfg.nli (default "Xenova/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7" / "onnx").
  • RagCfg.nli_threshold: f32 (default 0.0 = disabled — spec §2.6 single gate).
  • env override: KEBAB_MODELS_NLI_MODEL, KEBAB_MODELS_NLI_PROVIDER, KEBAB_RAG_NLI_THRESHOLD (malformed → keep prior + warn).

kebab-rag

  • RagPipeline.verifier: Option<Arc<dyn kebab_nli::NliVerifier>> field + with_verifier(self, v) -> Self builder — 둘 다 #[allow(dead_code)] 임시 attribute (round-2 critic M1, PR-9c-2 step 8.5 hook 가 read 활성화 시 제거).
  • RagPipeline::new 시그니처 unchanged (round-2 NEW-M1 Option B builder 결정).
  • crates/kebab-rag/Cargo.tomlkebab-nli path 의존 추가.

Wire schemas

  • answer.schema.json: verification property (anyOf [object, null]) + $defs.VerificationSummary + refusal_reason.enum 에 2 값 추가.
  • error.schema.json: code.enum 에 2 값 + details.description 의 forward-looking reservation.

docs/ARCHITECTURE.md

  • Mermaid Adapters subgraph 에 nli 노드 + edges:
    • rag → nli (이 PR 추가)
    • app → nli (forward-looking — PR-9c-2 가 facade 의 의존 추가)
    • nli → config
  • nli → core edge SKIPPED: crates/kebab-nli/Cargo.toml 의 direct deps 가 kebab-config 만 (transitive 로 core 도달). ARCHITECTURE 컨벤션 = direct deps only (round-2 document-specialist NIT-1 결정).
  • 디렉토리 트리에 crates/kebab-nli/ 추가.

Scope creep (compile-time forcing function)

RefusalReason 가 exhaustive match 패턴 사용 — 새 variant 2개가 다음 파일들의 match 가지 추가 강제:

  • crates/kebab-store-sqlite/src/answers.rs: 2 arms (snake_case DB labels).
  • crates/kebab-tui/src/ask.rs: 2 arms (TUI refusal display).

Answer.verification field 추가로 Answer 직접 생성 부분에 verification: None 추가:

  • crates/kebab-rag/src/pipeline.rs 의 6 construction sites (refuse helpers + happy path).
  • crates/kebab-cli/src/main.rs test fixture, crates/kebab-eval/src/metrics.rs test fixture, crates/kebab-tui/tests/ask.rs test fixture 각 1.

검증

  • cargo test -p kebab-core -j 163 passed (3 신규: serde snake_case rename + verification skip + VerificationSummary struct shape).
  • cargo test -p kebab-config -j 156 passed (6 신규: defaults + legacy parse + env override + malformed env).
  • cargo test -p kebab-rag -j 166 passed (Answer fixture 의 verification: None 추가 외 회귀 0).
  • cargo test -p kebab-cli --test wire_ask_multi_hop -j 19 passed (5 신규: schema verification field + $defs + refusal_reason enum 2 + error code enum 2).
  • cargo clippy --workspace --all-targets -j 1 -- -D warnings clean.
  • cargo test --workspace --no-fail-fast -j 1 → 172 test result blocks pass + pre-existing 1 flaky (kebab-mcp::tools_call_ask_multi_hop::ask_tool_routes_multi_hop_true_to_decompose_first — main baseline 동일, no_chunks short-circuit, NLI 무관, HOTFIXES candidate).

비범위

  • Pipeline integration (PR-9c-2 의 ask_multi_hop step 8.5 hook + truncate_for_nli helper + refuse helpers).
  • kebab-app 의 OnnxNliVerifier construction + facade invariant (PR-9c-2).
  • Mock NLI verifier test (PR-9c-2).
  • Dogfood retest (PR-9d).

시험 항목 (Test Plan)

  • cargo test --workspace -j 1 회귀 0 (pre-existing 1 flaky 동일).
  • cargo clippy --workspace --all-targets -j 1 -- -D warnings clean.
  • kebab-core: serde wire mapping NliVerificationFailed → "nli_verification_failed" 단위 검증.
  • kebab-config: NliCfg defaults + RagCfg.nli_threshold 0.0 + env override 6 unit pass.
  • kebab-cli wire: answer.v1 verification optional + $defs.VerificationSummary required 3 fields + refusal_reason enum 확장 + error.v1.code enum 확장 5 unit pass.
  • RagPipeline.verifier + with_verifier 의 #[allow(dead_code)] attribute (PR-9c-2 가 제거).
  • Wire additive minor — pre-v0.18 reader 무영향 (verification skip_serializing_if + refusal_reason enum 확장 backward-compat).

Assisted-by: Claude Code

## 요약 PR-9 의 wire + core 표면. behavior wiring 없음 — `ask_multi_hop` 의 NLI hook 은 PR-9c-2 가 활성화. 본 PR 머지 후 사용자가 `[rag] nli_threshold = 0.5` 를 config 에 설정 가능하지만 실 동작은 PR-9c-2 까지 dormant. 설계: docs/superpowers/specs/2026-05-25-p9-fb-41-finalize-spec.md (§3 PR-9c-1, §2.4~§2.6) 계획: docs/superpowers/plans/2026-05-25-p9-fb-41-finalize-plan.md (§4) ## 변경 사항 ### kebab-core - `RefusalReason::NliVerificationFailed` + `NliModelUnavailable` 추가 — `serde(rename_all="snake_case")` 가 wire string 자동 매핑. `answer.v1.refusal_reason` ↔ `error.v1.code` 동일 `"nli_verification_failed"` / `"nli_model_unavailable"`. - `Answer.verification: Option<VerificationSummary>` field 추가 (`#[serde(default, skip_serializing_if = "Option::is_none")]` — additive minor wire). - `VerificationSummary { nli_score: f32, nli_threshold: f32, nli_passed: bool }` 신규 struct. ### kebab-config - `NliCfg { model, provider }` 신규 + `ModelsCfg.nli` (default `"Xenova/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7"` / `"onnx"`). - `RagCfg.nli_threshold: f32` (default `0.0` = disabled — spec §2.6 single gate). - env override: `KEBAB_MODELS_NLI_MODEL`, `KEBAB_MODELS_NLI_PROVIDER`, `KEBAB_RAG_NLI_THRESHOLD` (malformed → keep prior + warn). ### kebab-rag - `RagPipeline.verifier: Option<Arc<dyn kebab_nli::NliVerifier>>` field + `with_verifier(self, v) -> Self` builder — 둘 다 `#[allow(dead_code)]` 임시 attribute (round-2 critic M1, PR-9c-2 step 8.5 hook 가 read 활성화 시 제거). - `RagPipeline::new` 시그니처 unchanged (round-2 NEW-M1 Option B builder 결정). - `crates/kebab-rag/Cargo.toml` 에 `kebab-nli` path 의존 추가. ### Wire schemas - `answer.schema.json`: `verification` property (`anyOf [object, null]`) + `$defs.VerificationSummary` + `refusal_reason.enum` 에 2 값 추가. - `error.schema.json`: `code.enum` 에 2 값 + `details.description` 의 forward-looking reservation. ### docs/ARCHITECTURE.md - Mermaid Adapters subgraph 에 `nli` 노드 + edges: - `rag → nli` (이 PR 추가) - `app → nli` (forward-looking — PR-9c-2 가 facade 의 의존 추가) - `nli → config` - **`nli → core` edge SKIPPED**: `crates/kebab-nli/Cargo.toml` 의 direct deps 가 `kebab-config` 만 (transitive 로 core 도달). ARCHITECTURE 컨벤션 = direct deps only (round-2 document-specialist NIT-1 결정). - 디렉토리 트리에 `crates/kebab-nli/` 추가. ### Scope creep (compile-time forcing function) `RefusalReason` 가 exhaustive match 패턴 사용 — 새 variant 2개가 다음 파일들의 match 가지 추가 강제: - `crates/kebab-store-sqlite/src/answers.rs`: 2 arms (snake_case DB labels). - `crates/kebab-tui/src/ask.rs`: 2 arms (TUI refusal display). 또 `Answer.verification` field 추가로 Answer 직접 생성 부분에 `verification: None` 추가: - `crates/kebab-rag/src/pipeline.rs` 의 6 construction sites (refuse helpers + happy path). - `crates/kebab-cli/src/main.rs` test fixture, `crates/kebab-eval/src/metrics.rs` test fixture, `crates/kebab-tui/tests/ask.rs` test fixture 각 1. ## 검증 - `cargo test -p kebab-core -j 1` → **63 passed** (3 신규: serde snake_case rename + verification skip + VerificationSummary struct shape). - `cargo test -p kebab-config -j 1` → **56 passed** (6 신규: defaults + legacy parse + env override + malformed env). - `cargo test -p kebab-rag -j 1` → **66 passed** (Answer fixture 의 verification: None 추가 외 회귀 0). - `cargo test -p kebab-cli --test wire_ask_multi_hop -j 1` → **9 passed** (5 신규: schema verification field + $defs + refusal_reason enum 2 + error code enum 2). - `cargo clippy --workspace --all-targets -j 1 -- -D warnings` clean. - `cargo test --workspace --no-fail-fast -j 1` → 172 test result blocks pass + **pre-existing 1 flaky** (`kebab-mcp::tools_call_ask_multi_hop::ask_tool_routes_multi_hop_true_to_decompose_first` — main baseline 동일, no_chunks short-circuit, NLI 무관, HOTFIXES candidate). ## 비범위 - Pipeline integration (PR-9c-2 의 `ask_multi_hop` step 8.5 hook + truncate_for_nli helper + refuse helpers). - `kebab-app` 의 OnnxNliVerifier construction + facade invariant (PR-9c-2). - Mock NLI verifier test (PR-9c-2). - Dogfood retest (PR-9d). ## 시험 항목 (Test Plan) - [x] cargo test --workspace -j 1 회귀 0 (pre-existing 1 flaky 동일). - [x] cargo clippy --workspace --all-targets -j 1 -- -D warnings clean. - [x] kebab-core: serde wire mapping `NliVerificationFailed → "nli_verification_failed"` 단위 검증. - [x] kebab-config: NliCfg defaults + RagCfg.nli_threshold 0.0 + env override 6 unit pass. - [x] kebab-cli wire: answer.v1 verification optional + $defs.VerificationSummary required 3 fields + refusal_reason enum 확장 + error.v1.code enum 확장 5 unit pass. - [x] RagPipeline.verifier + with_verifier 의 #[allow(dead_code)] attribute (PR-9c-2 가 제거). - [x] Wire additive minor — pre-v0.18 reader 무영향 (verification skip_serializing_if + refusal_reason enum 확장 backward-compat). Assisted-by: Claude Code
altair823 added 1 commit 2026-05-25 23:29:12 +00:00
Surface-only PR (no behavior wiring — that's PR-9c-2):
- kebab-core: RefusalReason::NliVerificationFailed + NliModelUnavailable (serde rename_all="snake_case", wire = identical strings).
- kebab-core: Answer.verification: Option<VerificationSummary> field (additive minor wire — pre-v0.18 reader 무영향).
- kebab-core: VerificationSummary { nli_score: f32, nli_threshold: f32, nli_passed: bool } struct + lib.rs 재-export.
- kebab-config: NliCfg { model, provider } + ModelsCfg.nli (default Xenova/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7).
- kebab-config: RagCfg.nli_threshold: f32 (default 0.0 = disabled, spec §2.6 single gate).
- kebab-config: env override KEBAB_MODELS_NLI_MODEL/PROVIDER + KEBAB_RAG_NLI_THRESHOLD (parse 실패 시 tracing::warn + default 유지).
- kebab-rag: RagPipeline.verifier: Option<Arc<dyn NliVerifier>> field + with_verifier builder (모두 #[allow(dead_code)] — PR-9c-2 의 step 8.5 hook 가 활성화 시 제거). RagPipeline::new signature 유지 (round-2 NEW-M1 Option B).
- kebab-rag: Cargo.toml 에 kebab-nli path 의존 추가.
- kebab-store-sqlite + kebab-tui: 두 신규 RefusalReason variant 에 대한 exhaustive match arm 추가 (snake_case label / 표시 문구).
- 모든 Answer 구축 site (rag 6 + cli/tui/eval 3 fixture) 에 verification: None 추가.
- wire schemas: answer.schema.json verification field + \$defs.VerificationSummary + refusal_reason.enum 2 추가. error.schema.json code.enum + details.description 2 추가 (forward-looking reserved).
- docs/ARCHITECTURE.md: Mermaid Adapters subgraph 의 nli 노드 + rag→nli + app→nli (forward-looking) + nli→config edges. nli→core edge 는 skip (kebab-nli/Cargo.toml direct dep 가 config 만, ARCHITECTURE 컨벤션 = direct deps only). 디렉토리 트리에 crates/kebab-nli/ 추가.

Tests: kebab-core 3 (serde rename + verification skip + struct shape) + kebab-config 6 (defaults + legacy + env + malformed env) + kebab-cli wire 5 (schema verification + enum 검증).
검증: cargo test --workspace -j 1 회귀 0 (pre-existing kebab-mcp::tools_call_ask_multi_hop flaky 1개 동일 — spec 에 명시된 known-flaky). cargo clippy --workspace --all-targets -D warnings clean.
Wire 영향: additive minor — answer.v1 의 verification optional + refusal_reason.enum 확장 + error.v1.code 확장.

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

회차 1 — surface-only 표면 PR 검토.

칭찬 (의도적 inline 안 함, 산문):

  1. RefusalReason 두 variant 의 wire 일관성 검증 — kebab-core 의 단위 test refusal_reason_nli_variants_serialize_to_snake_case 가 serde rename_all="snake_case" 의 wire mapping 을 직접 assert. spec §2.4 의 "wire string identical" rule 의 single source of truth 확인.

  2. answer.v1 의 additive minor 검증 강제answer_omits_verification_field_when_none test 가 serde_json::to_value(&Answer { verification: None, ... }) 에서 \"verification\" key 가 emit 안 되는지 assert. pre-v0.18 reader 의 backward-compat 회귀 핀.

  3. KEBAB_RAG_NLI_THRESHOLD malformed 의 explicit warn — 다른 numeric env override 의 silent ignore pattern 과 의도적으로 다름. comment 가 이유 설명 ("silently disable a security-flavored gate the user thought they enabled — failure mode the malformed value cannot be allowed to produce silently"). 디펜시브 + 사용자 visible. 좋은 결정.

  4. #[allow(dead_code)] 의 명시적 PR-9c-2 reference docRagPipeline.verifier field + with_verifier builder 의 doc 가 "the attribute is removed in the PR-9c-2 commit" 명시. transitional 의도 표면화 — 미래 작업자가 attribute 제거 시점 의심 없음.

  5. DB label = wire string identicalkebab_store_sqlite::refusal_reason_label 의 두 새 arms 가 \"nli_verification_failed\" / \"nli_model_unavailable\" snake_case — wire = DB = error code 의 single source of truth 일관.

  6. nli → core edge 의 의식적 SKIPcrates/kebab-nli/Cargo.toml 의 direct deps 가 kebab-config 만 (core 는 transitive). ARCHITECTURE.md convention = direct deps only 의 정확 적용. round-2 document-specialist NIT-1 closure.

  7. Scope creep 의 honest 표면화 — kebab-store-sqlite + kebab-tui + kebab-eval + kebab-cli 의 exhaustive match arm 추가 + Answer 직접 생성 부분의 verification: None 명시. compile-time forcing function 의 합리적 대응 — PR description 의 "## Scope creep" 절에 명시적 trace.

minor advisory 1건 inline. carry-forward to PR-9c-2 — 본 PR 변경 없음.

전체 production quality. 추가 actionable 없음. PR-9c-2 가 ask_multi_hop step 8.5 hook + truncate_for_nli helper + App facade NLI construction 추가할 자연스러운 baseline.

머지 OK.

회차 1 — surface-only 표면 PR 검토. 칭찬 (의도적 inline 안 함, 산문): 1. **RefusalReason 두 variant 의 wire 일관성 검증** — kebab-core 의 단위 test `refusal_reason_nli_variants_serialize_to_snake_case` 가 serde rename_all=\"snake_case\" 의 wire mapping 을 직접 assert. spec §2.4 의 \"wire string identical\" rule 의 single source of truth 확인. 2. **answer.v1 의 additive minor 검증 강제** — `answer_omits_verification_field_when_none` test 가 `serde_json::to_value(&Answer { verification: None, ... })` 에서 `\"verification\"` key 가 emit *안 되는지* assert. pre-v0.18 reader 의 backward-compat 회귀 핀. 3. **`KEBAB_RAG_NLI_THRESHOLD` malformed 의 explicit warn** — 다른 numeric env override 의 silent ignore pattern 과 *의도적으로 다름*. comment 가 이유 설명 (\"silently disable a security-flavored gate the user thought they enabled — failure mode the malformed value cannot be allowed to produce silently\"). 디펜시브 + 사용자 visible. 좋은 결정. 4. **`#[allow(dead_code)]` 의 명시적 PR-9c-2 reference doc** — `RagPipeline.verifier` field + `with_verifier` builder 의 doc 가 \"the attribute is removed in the PR-9c-2 commit\" 명시. transitional 의도 표면화 — 미래 작업자가 attribute 제거 시점 의심 없음. 5. **DB label = wire string identical** — `kebab_store_sqlite::refusal_reason_label` 의 두 새 arms 가 `\"nli_verification_failed\"` / `\"nli_model_unavailable\"` snake_case — wire = DB = error code 의 single source of truth 일관. 6. **`nli → core` edge 의 의식적 SKIP** — `crates/kebab-nli/Cargo.toml` 의 direct deps 가 `kebab-config` 만 (core 는 transitive). ARCHITECTURE.md convention = direct deps only 의 정확 적용. round-2 document-specialist NIT-1 closure. 7. **Scope creep 의 *honest* 표면화** — kebab-store-sqlite + kebab-tui + kebab-eval + kebab-cli 의 exhaustive match arm 추가 + Answer 직접 생성 부분의 `verification: None` 명시. compile-time forcing function 의 합리적 대응 — PR description 의 \"## Scope creep\" 절에 명시적 trace. minor advisory 1건 inline. *carry-forward to PR-9c-2* — 본 PR 변경 없음. 전체 production quality. 추가 actionable 없음. PR-9c-2 가 `ask_multi_hop` step 8.5 hook + truncate_for_nli helper + App facade NLI construction 추가할 자연스러운 baseline. 머지 OK.
@@ -218,3 +230,4 @@
verifier: None,
}
}

N1 (minor): with_verifier doc 의 "Currently unused — PR-9c-2 wires the read site" 가 production code 의 implementation note. #[allow(dead_code)] attribute 가 PR-9c-2 에서 제거될 때 doc 의 PR-9c-2 reference 도 함께 정리 필요. 즉 그 시점에 doc 를 "NLI verifier 를 RagPipeline 에 wire — App::open_with_config 가 config.rag.nli_threshold > 0 일 때 호출" 같은 final form 으로. 본 PR-9c-1 에서 변경 안 함 — PR-9c-2 carry-forward reminder.

severity minor (의도적 transitional doc, PR-9c-2 closure scope).

**N1 (minor)**: `with_verifier` doc 의 "Currently unused — PR-9c-2 wires the read site" 가 production code 의 *implementation note*. `#[allow(dead_code)]` attribute 가 PR-9c-2 에서 제거될 때 *doc 의 PR-9c-2 reference 도 함께 정리* 필요. 즉 그 시점에 doc 를 "NLI verifier 를 RagPipeline 에 wire — App::open_with_config 가 config.rag.nli_threshold > 0 일 때 호출" 같은 *final form* 으로. 본 PR-9c-1 에서 변경 안 함 — *PR-9c-2 carry-forward reminder*. severity minor (의도적 transitional doc, PR-9c-2 closure scope).
altair823 merged commit 681c48b2a3 into main 2026-05-26 00:09:06 +00:00
altair823 deleted branch feat/fb-41-pr-9c-1-core-types-wire 2026-05-26 00:09:07 +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#178