feat(cli): kebab ask citation block (p9-fb-20) #64

Merged
altair823 merged 2 commits from feat/p9-fb-20-citation into main 2026-05-03 00:40:50 +00:00
Owner

Summary

도그푸딩 항목 16 (citation 풀 경로 + scroll) 의 CLI part. 답변 출력 후 근거: 절 — [N] <full path>#<fragment> (score=<s>) 한 줄씩. 사용자가 narrow terminal 에서 잘리던 inline citation 형식 ([1] notes/foo.md#L12-L34) 대신 full path 한 줄 단위.

변경

CLI

  • Cmd::Ask 에 두 flag 추가:
    • --show-citations — default ON, conflicts_with hide.
    • --hide-citations — 답변 본문만 출력 (pipe 시 trailing metadata 회피).
  • handler: !cli.json && show && !hide && !ans.citations.is_empty() 시 citation block 출력. 형식:
    근거:
      [1] crates/kebab-app/src/lib.rs#L120-L140  (score=0.78)
      [2] notes/foo.md#L12-L34                    (score=0.78)
    
  • --json 무영향 — citations 가 wire payload 에 항상 포함.

미적용 (out of scope)

  • TUI citation pane 의 turn 별 fold + Enter/o jump + i inspect — TUI 는 P9-3 의 기존 render_citations_or_explain 가 일부 cover. 추가 surface 는 후속 task (사용자 도그푸딩 priority 5위 의 핵심 = full path 가독성 = CLI block 으로 충족).

Test plan

  • cargo check -p kebab-cli clean.
  • cargo clippy -p kebab-cli --all-targets -- -D warnings clean.
  • [-] integration test 누락 — kebab ask integration 은 Ollama mock 없이 어려움 (P9-3 ask_smoke 도 같은 한계). flag parsing + format 검증은 다음 PR 에서 add 가능.

후속

  • 머지 후 spec status in_progresscompleted.
  • TUI citation pane 추가 (fold + jump + inspect) 별 task 로 분리.
## Summary 도그푸딩 항목 16 (citation 풀 경로 + scroll) 의 CLI part. 답변 출력 후 `근거:` 절 — `[N] <full path>#<fragment> (score=<s>)` 한 줄씩. 사용자가 narrow terminal 에서 잘리던 inline citation 형식 (`[1] notes/foo.md#L12-L34`) 대신 full path 한 줄 단위. ## 변경 ### CLI - `Cmd::Ask` 에 두 flag 추가: - `--show-citations` — default ON, conflicts_with hide. - `--hide-citations` — 답변 본문만 출력 (pipe 시 trailing metadata 회피). - handler: `!cli.json && show && !hide && !ans.citations.is_empty()` 시 citation block 출력. 형식: ``` 근거: [1] crates/kebab-app/src/lib.rs#L120-L140 (score=0.78) [2] notes/foo.md#L12-L34 (score=0.78) ``` - `--json` 무영향 — citations 가 wire payload 에 항상 포함. ## 미적용 (out of scope) - TUI citation pane 의 turn 별 fold + Enter/o jump + i inspect — TUI 는 P9-3 의 기존 `render_citations_or_explain` 가 일부 cover. 추가 surface 는 후속 task (사용자 도그푸딩 priority 5위 의 핵심 = full path 가독성 = CLI block 으로 충족). ## Test plan - [x] `cargo check -p kebab-cli` clean. - [x] `cargo clippy -p kebab-cli --all-targets -- -D warnings` clean. - [-] integration test 누락 — `kebab ask` integration 은 Ollama mock 없이 어려움 (P9-3 ask_smoke 도 같은 한계). flag parsing + format 검증은 다음 PR 에서 add 가능. ## 후속 - 머지 후 spec status `in_progress` → `completed`. - TUI citation pane 추가 (fold + jump + inspect) 별 task 로 분리.
altair823 added 1 commit 2026-05-03 00:37:54 +00:00
답변 출력 후 `근거:` 절 — `[N] <full path>#<fragment>  (score=<s>)`
한 줄씩. spec p9-fb-20 의 핵심 (full path 가독성) 충족.

신규 flag:
- --show-citations: default ON. 답변 후 citation block 출력.
- --hide-citations: 답변 본문만 출력 (pipe 시 다른 도구가 trailing
  metadata 안 받기 원할 때).

`--json` 모드 무영향 — citations 가 wire payload 에 항상 포함되므로
flag 가 영향 X (외부 wrapper 호환성).

spec p9-fb-20 의 \"TUI citation pane + jump (Enter/o editor jump,
i inspect)\" 부분은 본 PR scope 에서 제외 — TUI 의 기존
render_citations_or_explain (P9-3) 가 이미 citation list 표시,
추가 fold/jump 는 후속 task. 사용자 도그푸딩 priority 5위 의
핵심 = \"full path 가독성\" 이라 CLI block 만으로 충분.

Plan 갱신:
- p9-fb-20 status planned → in_progress. 머지 후 한 줄 commit
  으로 completed flip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 requested changes 2026-05-03 00:39:30 +00:00
Dismissed
claude-reviewer-01 left a comment
Member

회차 1 — information accuracy 1건 + cleanup nit 1건 + 칭찬 3건.

핵심 actionable:

  1. (misleading info) ans.retrieval.top_score 를 모든 citation 라인에 반복 출력 — 사용자 오해 위험 (per-citation score 같다?). AnswerCitation 에 per-citation score 없으니 score 컬럼 제거 + retrieval 메타 한 줄로 분리 권장.
  2. (cleanup) marker fallback 의 두 번 변환 구조 → c.marker.clone().unwrap_or_else(...) 한 단계.

총평: --show/--hide flag 패턴 + --json 무영향 명시 + spec scope 분리 정직 — 칭찬. 위 actionable 2건만 정리하면 머지.

회차 1 — information accuracy 1건 + cleanup nit 1건 + 칭찬 3건. 핵심 actionable: 1. **(misleading info)** `ans.retrieval.top_score` 를 모든 citation 라인에 반복 출력 — 사용자 오해 위험 (per-citation score 같다?). `AnswerCitation` 에 per-citation score 없으니 score 컬럼 제거 + retrieval 메타 한 줄로 분리 권장. 2. **(cleanup)** marker fallback 의 두 번 변환 구조 → `c.marker.clone().unwrap_or_else(...)` 한 단계. 총평: --show/--hide flag 패턴 + --json 무영향 명시 + spec scope 분리 정직 — 칭찬. 위 actionable 2건만 정리하면 머지.

(칭찬) --show-citations (default ON, conflicts_with hide) + --hide-citations 두 flag 패턴 — clap 의 conflicts_with 가 mutually-exclusive 강제, default ON 이라 사용자 별도 입력 없이 citation block 자동 출력. --hide-citations 한 번 typing 으로 pipe 시 답변 본문만. p9-fb-06 의 reset scope flag 의 ArgGroup 패턴과 일관.

(칭찬) `--show-citations` (default ON, conflicts_with hide) + `--hide-citations` 두 flag 패턴 — clap 의 `conflicts_with` 가 mutually-exclusive 강제, default ON 이라 사용자 별도 입력 없이 citation block 자동 출력. `--hide-citations` 한 번 typing 으로 pipe 시 답변 본문만. p9-fb-06 의 reset scope flag 의 ArgGroup 패턴과 일관.

(칭찬) --json 무영향 — citations 가 wire payload (answer.v1) 에 항상 ���함. 사람-친화 출력 toggle 만 flag 영향, 외부 wrapper (Claude Code skill / MCP) 의 contract 변경 0. 코멘트 "citations are always included in the wire payload regardless of this flag" 가 의도 명시.

(칭찬) `--json` 무영향 — citations 가 wire payload (`answer.v1`) 에 항상 ���함. 사람-친화 출력 toggle 만 flag 영향, 외부 wrapper (Claude Code skill / MCP) 의 contract 변경 0. 코멘트 "citations are always included in the wire payload regardless of this flag" 가 의도 명시.

(cleanup / 두 번 변환) c.marker.as_deref().unwrap_or(&format!(...)).to_string()format! 가 String 만들고 & 으로 ref, as_derefOption<&str>, unwrap_or&str, 다시 to_string — 두 번 변환.

Why: 동작 OK 지만 reader 가 한 줄에 두 번 conversion 보면 "왜?" 의문.

How to apply: let marker = c.marker.clone().unwrap_or_else(|| format!("{}", idx + 1)); — 한 단계로 끝남.

(cleanup / 두 번 변환) `c.marker.as_deref().unwrap_or(&format!(...)).to_string()` — `format!` 가 String 만들고 `&` 으로 ref, `as_deref` 가 `Option<&str>`, `unwrap_or` 가 `&str`, 다시 `to_string` — 두 번 변환. Why: 동작 OK 지만 reader 가 한 줄에 두 번 conversion 보면 "왜?" 의문. How to apply: `let marker = c.marker.clone().unwrap_or_else(|| format!("{}", idx + 1));` — 한 단계로 끝남.

(information accuracy) ans.retrieval.top_score 를 모든 citation 라인에 반복 출력 — 사용자가 N 개 citation 모두 같은 score 보면서 "per-citation 점수 같다?" 오해. top_score 는 retrieval 의 max 한 값 — citation 별 점수 아님.

현재 kebab_core::AnswerCitation { marker, citation } 가 per-citation score 안 가짐 (Citation enum 도 위치 정보만). per-citation score 노출하려면 facade / Answer struct 변경 필요 — 본 PR scope 밖.

Why: misleading 정보 노출보다 정보 누락이 정직. spec 본문의 (score=0.78, doc_id=abc123) 형식은 미래 facade 확장 후 가능 — 본 PR 은 score 컬럼 자체 제거 + retrieval summary 한 줄로 분리 권장.

How to apply: 출력 형식을 [N] <full path>#<fragment> 단순으로 변경, citation block 아래에 한 줄 (retrieval: top_score=X.XX, k=N, used=N/M) 으로 retrieval 메타 분리. per-citation score 는 후속 task — AnswerCitationscore: Option<f32> 추가 + RAG facade 가 SearchHit 와 매칭 시 채움.

(information accuracy) `ans.retrieval.top_score` 를 모든 citation 라인에 반복 출력 — 사용자가 N 개 citation 모두 같은 score 보면서 "per-citation 점수 같다?" 오해. `top_score` 는 retrieval 의 max 한 값 — citation 별 점수 아님. 현재 `kebab_core::AnswerCitation { marker, citation }` 가 per-citation score 안 가짐 (Citation enum 도 위치 정보만). per-citation score 노출하려면 facade / Answer struct 변경 필요 — 본 PR scope 밖. Why: misleading 정보 노출보다 정보 누락이 정직. spec 본문의 `(score=0.78, doc_id=abc123)` 형식은 미래 facade 확장 후 가능 — 본 PR 은 score 컬럼 자체 제거 + retrieval summary 한 줄로 분리 권장. How to apply: 출력 형식을 `[N] <full path>#<fragment>` 단순으로 변경, citation block 아래에 한 줄 `(retrieval: top_score=X.XX, k=N, used=N/M)` 으로 retrieval 메타 분리. per-citation score 는 후속 task — `AnswerCitation` 에 `score: Option<f32>` 추가 + RAG facade 가 SearchHit 와 매칭 시 채움.
@@ -4,3 +4,3 @@
task_id: p9-fb-20
title: "Citation full path + scrollable pane (CLI block + TUI pane + jump)"
status: planned
status: in_progress

(칭찬) PR body + HANDOFF entry 가 spec scope 의 일부 (TUI fold + Enter/o jump + i inspect) 미적용을 정직 명시 + 후속 task 약속. p9-fb-04 의 "CLI Ctrl-C subprocess test 미적용" 패턴과 일관 — 본 PR 가 단일 PR scope 안에서 충분한 가치 제공 (full path 가독성 = 사용자 priority 5위 핵심) + 추가 surface 는 미래 PR. spec 본문 무수정.

(칭찬) PR body + HANDOFF entry 가 spec scope 의 일부 (TUI fold + Enter/o jump + i inspect) 미적용을 정직 명시 + 후속 task 약속. p9-fb-04 의 "CLI Ctrl-C subprocess test 미적용" 패턴과 일관 — 본 PR 가 단일 PR scope 안에서 충분한 가치 제공 (full path 가독성 = 사용자 priority 5위 핵심) + 추가 surface 는 미래 PR. spec 본문 무수정.
altair823 added 1 commit 2026-05-03 00:40:19 +00:00
회차 1 actionable 2건 반영.

- (information accuracy) 모든 citation 라인이 같은 ans.retrieval.top_score
  반복 출력했던 문제 — AnswerCitation 에 per-citation score 없으므로
  사용자 오해 회피 위해 score 컬럼 제거. 대신 retrieval 메타 한 줄로
  분리: '(retrieval: top_score=X.XX, k=N, used=M/N)'. per-citation
  score 노출은 facade + AnswerCitation 의 미래 확장 후 (별 task).
- (cleanup) marker fallback 의 두 번 변환 (as_deref + unwrap_or +
  to_string) → c.marker.clone().unwrap_or_else(|| format!(...))
  한 단계로 단순화.

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

회차 2 — actionable 2건 (per-citation score 제거 + retrieval 메타 한 줄로 분리 / marker fallback 단순화) 정확히 반영. APPROVE.

회차 2 — actionable 2건 (per-citation score 제거 + retrieval 메타 한 줄로 분리 / marker fallback 단순화) 정확히 반영. APPROVE.
altair823 merged commit c42b4bc467 into main 2026-05-03 00:40:50 +00:00
altair823 deleted branch feat/p9-fb-20-citation 2026-05-03 00:40:51 +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#64