회차 2 — 회차 1 의 actionable 0 (모두 PR-3b carry-over) 확인. 본 PR 의 코드 변경 불필요. APPROVE.
Wire schema additionalProperties 확인 결과:
Suggestion — HopRecord.sub_queries doc 의 "this iter" ambiguity (PR-3b 시점에 보강):
회차 1 — PR-3a 의 wire additive + cfg 노브 foundation 깔끔 ship. RAG pipeline 미변경 + skip_serializing_if 가 backward-compat invariant 유지. actionable 0 (모두 PR-3b carry-over) + suggestion 3 + question 1 (self-resolved) + 칭찬 1:
칭찬 — Answer.hops: Option<Vec<HopRecord>> 의 #[serde(default, skip_serializing_if = "Option::is_none")] annotation 가 backward-compat invariant 정확히 표현. (1) None 일 때 emit 안 됨 → 옛 single-pass consumer wire 변동 0, (2) serde(default) 로 옛 wire 가 본 schema 로 deserialize 시 None 자동, (3) Option<Vec<HopRecord>> 가 "hops 정보가 없을 수도 있다" 와 "hops 가 있지만 비어 있다" 를 type-level 로 구분 (multi-hop 일 때만 Some, refusal 이라도 trace 가 1+ entry 보존 시 Some(vec![...]) 가능).
Suggestion — MULTI_HOP_MAX_SUB_QUERIES_DEFAULT (const) vs RagCfg.multi_hop_max_sub_queries_per_iter (cfg) reconciliation:
Suggestion — RagCfg.multi_hop_* field 의 flat vs hierarchic naming 결정:
칭찬 — parse_decompose_response_drops_partial_empty_keeps_valid 핀이 정확히 trim+filter chain 의 invariant ("["", "valid q", " "] → ["valid q"]") 를 표현. 향후 refactor (예: PR-3 의 helper 추출 시 step 재정렬) 가 partial-empty 케이스를 silently 깨트리면 즉시 fail.
회차 2 — 회차 1 의 actionable 1 (partial-empty pin) + question 답변 (stop intent doc) 잘 반영. 잔여 actionable 0건. APPROVE.
칭찬 — stop: Vec::new() 옆 doc comment 가 (1) instruction-following 모델 가정 + (2) prose-after-array 시 의도된 refusal policy + (3) 잘못된 stop sequence (]) 의 truncation 위험까지 명시. 후속 reviewer / archeology 가 Vec::new() 의도 즉시 파악 + alternative trade-off 까지 추적 가능. 회차 1 question 의 답변이 코드 + doc 양쪽에 etched 됨.
Corner case (nit) — MULTI_HOP_DECOMPOSE_USER_TEMPLATE substitution order:
Question — decompose stop vec 비어 있는 의도 확인:
회귀 핀 보강 권장: partial-empty 케이스. 현재 parse_decompose_response_returns_none_for_garbage 는 모두 empty 인 [" ", ""] → None 검증. ["", "valid"] → Some(vec!["valid"]) (empty drop, 나머지 유지) 케이스의 회귀 핀 없음.
회차 1 — PR-2 scope 정확히 ship (Default + dispatch + decompose-failure refusal + cascade). RefusalReason cascade build/clippy clean. actionable 1 + suggestion 3 + question 1 + 칭찬 1:
Refactor opportunity (PR-3 합리적, 본 PR scope 외) — ask_multi_hop 의 §4-§9 (pack_context → synthesize prompt → generate stream → citation extract → Answer build → persist) 가 ask 의 동일 단계 mirror. ~150 lines 중복.
칭찬 — RefusalReason::MultiHopDecomposeFailed variant 추가 후 모든 exhaustive match cascade (kebab-store-sqlite::refusal_reason_label + kebab-tui::ask refusal render) 가 build + clippy clean — Rust 의 exhaustive match 가 자동 guard. cascade missing 한 곳 없음. 또 wire code 의 snake_case 매핑 (multi_hop_decompose_failed) 이 design §10.1 error.v1 의 future-extensions 절 패턴과 일관.
회차 2 — 회차 1 의 actionable 2 + suggestion 1 모두 잘 반영. 잔여 actionable 0건. APPROVE.
칭찬 — mh-s-004 의 ["INSERT", "i 입력모드"] 보강이 두 토큰 모두 corpus 에 실제 등장 (README 의 "i 입력모드" literal + multiple doc 의 "INSERT"). 한국어 + 영어 mix 도 trigram 색인 + answer 본문 substring 검증 양쪽에 견고.
칭찬 — 3 question 영어 변환 (mh-c-005 schema list, mh-i-001 chunk_id recipe, mh-s-002 license) 이 자연스러운 영어 + 동일 must_contain 검증력 유지. 영어 dogfood 시 measurement gap 차단.
칭찬 — plan PR-1 단락이 실제 변경 + deviation 명시 양쪽 cover. "plan 초안 대비 deviation" sub-section 가 후속 archeology 가 "왜 plan 과 실제 PR 가 달랐는가" 즉시 파악 가능 — frozen-as-historical-contract 정책 유지하면서 실제 산출 추적. 다른 PR 들의 plan 단락도 동일 패턴이면 좋겠음 (PR-2~PR-6 에서도 deviation 발견 시).