feat(rag): fb-41 PR-9c-1 — core types + wire scaffolding (NLI verification) #178
Reference in New Issue
Block a user
Delete Branch "feat/fb-41-pr-9c-1-core-types-wire"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
요약
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(default0.0= disabled — spec §2.6 single gate).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) -> Selfbuilder — 둘 다#[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-nlipath 의존 추가.Wire schemas
answer.schema.json:verificationproperty (anyOf [object, null]) +$defs.VerificationSummary+refusal_reason.enum에 2 값 추가.error.schema.json:code.enum에 2 값 +details.description의 forward-looking reservation.docs/ARCHITECTURE.md
nli노드 + edges:rag → nli(이 PR 추가)app → nli(forward-looking — PR-9c-2 가 facade 의 의존 추가)nli → confignli → coreedge 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.verificationfield 추가로 Answer 직접 생성 부분에verification: None추가:crates/kebab-rag/src/pipeline.rs의 6 construction sites (refuse helpers + happy path).crates/kebab-cli/src/main.rstest fixture,crates/kebab-eval/src/metrics.rstest fixture,crates/kebab-tui/tests/ask.rstest 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 warningsclean.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).비범위
ask_multi_hopstep 8.5 hook + truncate_for_nli helper + refuse helpers).kebab-app의 OnnxNliVerifier construction + facade invariant (PR-9c-2).시험 항목 (Test Plan)
NliVerificationFailed → "nli_verification_failed"단위 검증.Assisted-by: Claude Code
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>회차 1 — surface-only 표면 PR 검토.
칭찬 (의도적 inline 안 함, 산문):
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 확인.answer.v1 의 additive minor 검증 강제 —
answer_omits_verification_field_when_nonetest 가serde_json::to_value(&Answer { verification: None, ... })에서\"verification\"key 가 emit 안 되는지 assert. pre-v0.18 reader 의 backward-compat 회귀 핀.KEBAB_RAG_NLI_THRESHOLDmalformed 의 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. 좋은 결정.#[allow(dead_code)]의 명시적 PR-9c-2 reference doc —RagPipeline.verifierfield +with_verifierbuilder 의 doc 가 "the attribute is removed in the PR-9c-2 commit" 명시. transitional 의도 표면화 — 미래 작업자가 attribute 제거 시점 의심 없음.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 일관.nli → coreedge 의 의식적 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.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_hopstep 8.5 hook + truncate_for_nli helper + App facade NLI construction 추가할 자연스러운 baseline.머지 OK.
@@ -218,3 +230,4 @@verifier: None,}}N1 (minor):
with_verifierdoc 의 "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).