v0.20.1 전체 도그푸딩에서 발견된 8 todo (Ask 응답언어 rag-v3 / doc.lang docs / bulk input / list title / fusion_score·score_kind / schema index_version / Ollama hint) 를 단일 patch release 로 설계. writer worker 초안 → opus critic round-1 리뷰 반영: - B1: top-level score placeholder → 확정 (score_kind 가 의미 선언, search.rs:95-99) - M1: 이미지 caption 언어 강제 out-of-scope 명시 - M2: config default 테스트(lib.rs:1316) 갱신 필요 명시 - M3: bulk input 전체 필드 (query/mode/k/trust_min/ingested_after/media/tag/lang) - M4: rag-v3 의 eval_runs.config_snapshot_json cascade 영향 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
20 KiB
title, created, status, contract_sections, parent_handoff
| title | created | status | contract_sections | parent_handoff | |||
|---|---|---|---|---|---|---|---|
| v0.20.2 — full dogfood findings (8 todo patch release) | 2026-05-28 | accepted |
|
docs/superpowers/handoffs/2026-05-28-v0.20.1-fulldogfood-findings-handoff.md |
v0.20.2 — full dogfood findings (8 todo patch release)
1. Summary
v0.20.1 full dogfood run (/build/dogfood/corpus 6293 file → 3940 docs / 34896 chunks) 에서 발견된 8개 finding 을 단일 patch release (0.20.2) 로 수렴. P0: 3개 (behavior + docs), P1: 3개 (docs drift), P2: 2개 (setup hint). 중심은 RAG response language auto-matching (Todo #1), bulk search input schema 명확화 (Todo #2), 그리고 wire schema + config 문서화 정확화.
2. Background
2.1 v0.20.1 Release 및 dogfood 환경
- v0.20.1:
PR#191merge (V009 한국어 morphological tokenizer + N-gram supplement + eager backfill). 2026-05-28 fresh full ingest. - dogfood data:
/build/dogfood/corpus/(6293 source file, format/category 별 분류) → 3940 docs, 34896 chunks. - dogfood KB:
/build/dogfood/kb/(SQLite + LanceDB + assets). - test coverage:
docs/DOGFOOD.md§1~§11 시나리오 + handoff §3 regression 시나리오 전체 실행.
2.2 발견된 finding 8개
각 finding 은 user-visible behavior 또는 docs/wire schema drift. brainstorming 은 이미 completed (handoff §1 + user confirmation).
- Finding O (Todo #1): Ask 영어 query → 한국어 response (RAG prompt template 한계)
- Finding V (Todo #2): bulk search input format 불명확 (
{"text":...}vs{"query":...}) - Finding Q (Todo #3): list docs title 중복 (heading-based title 추출 unique 부족)
- Finding U (Todo #4): doc.lang = "und" 53% (code file 의 자연 언어 감지 설계 의도 미문서화)
- Finding H/L (Todo #5): fusion_score 위치 오표기 (top-level 같이 표기되나 실제
.retrieval.fusion_score) - Finding X (Todo #6): score_kind 의 mode 별 의미 미명시 (lexical mode 에서
fusion_score == lexical_score) - Finding G (Todo #7): schema --json 의
index_version의미 혼동 (lexical index ≠ vector store index) - Todo #8: Ollama endpoint default localhost (init hint 부재)
3. Goals + Non-Goals
Goals
- User-visible behavior 및 wire schema 정확성 검증 완료.
- RAG response language auto-matching (영어 query → 영어 response, 한국어 → 한국어).
- bulk search input shape 명확화 + error message hint.
- list docs 출력에서 title 중복 제거 (doc_path 보조 표시).
- docs/wire schema 에서 fusion_score, score_kind, index_version 의미 정리.
- config.toml init hint 에 Ollama remote endpoint 안내 추가.
- 모든 finding 을 patch release 한 건으로 번들링.
Non-Goals
- wire schema major bump (v1 → v2) — all changes additive or docs-only.
- RAG
--response-langflag 도입 (Non-goal per brief). - code 주석 자연 언어 감지 (Non-goal, V010 migration 비용 高).
- Search ranking algorithm 변경.
- Embedding model 또는 vector search 변경.
4. Design Decisions (8 todo)
Todo #1 — Ask 응답 언어 (RAG prompt template) [P0]
Problem: crates/kebab-rag/src/pipeline.rs line 1874~1878 의 SYSTEM_PROMPT_RAG_V1/SYSTEM_PROMPT_RAG_V2 가 통째로 한국어 prose. MULTI_HOP_SYNTHESIZE_SYSTEM_PROMPT (line 1872 부근) 도 한국어. 응답 언어를 지정하는 규칙이 없어 gemma4 가 영어 query 에도 한국어로 답함.
Decision: Query 언어 자동 매칭 (user confirmation 완료).
Implementation:
-
New system prompt:
SYSTEM_PROMPT_RAG_V3— rag-v2 의 7규칙 + 언어 매칭 규칙 1개.- 위치:
crates/kebab-rag/src/pipeline.rsline 1880 부근. - 규칙 문구 (예시, 다듬음 권장):
답변은 [원본 질문]과 같은 언어로 작성한다. 단 [근거]에서 큰따옴표로 직접 인용하는 부분은 원문 언어 그대로 둔다. - rag-v2 는 그대로 보존 (legacy backwards-compat).
- 위치:
-
system_prompt_for() 함수 갱신 (line 1883 부근):
- match arm 에
"rag-v3"추가. - unknown 에러 메시지의 expected 목록도 v3 포함 갱신.
- match arm 에
-
MULTI_HOP_SYNTHESIZE_SYSTEM_PROMPT갱신 (line 1872):- 동일 언어 매칭 규칙 추가.
- decompose/decide 는 JSON array 출력이라 응답 언어와 무관 → 제외.
-
config default 갱신 (
crates/kebab-config/src/lib.rs):prompt_template_versiondefault"rag-v2"→"rag-v3".- user TOML 에서
prompt_template_version = "rag-v2"명시 시 legacy 유지.
-
frozen contract 갱신 (
docs/superpowers/specs/2026-04-27-kebab-final-form-design.md):- line 287: wire 예시의
prompt_template_version: "rag-v1"→"rag-v3"(또는 다양한 버전 예시). - line 899: rag-v2 는 legacy backwards-compat 으로 보존 표기.
- line 1349: config 예시의
prompt_template_version = "rag-v2"→"rag-v3". - line 1533: §9 cascade table 의
prompt_template_version행에 v3 mention.
- line 287: wire 예시의
Trade-off: 한국어 corpus 를 영어로 물으면 LLM 이 근거를 영어로 번역해 답함. citation [#번호] 로 원문 추적은 유지.
Out-of-scope — 다른 prompt 의 언어 동작 (M1): 이미지 caption (crates/kebab-parse-image/src/caption.rs:199-216 build_prompt(lang_hint)) 은 lang_hint = ko/kor 시 한국어, 그 외 영어로 강제하는 별도 메커니즘을 이미 가짐 — lang_hint 기반 + ingest 시점 + 자체 prompt_template_version cascade (caption.rs:132) 라 query-언어 매칭(본 Todo)과 메커니즘이 다르며 본 release scope 밖. OCR 텍스트 추출은 prompt 없음 (엔진 직접). 이 결정을 명시해 향후 "다른 prompt 에 언어 강제 없음" 암묵 가정으로 인한 drift 추적 불가를 방지.
Unit test: system_prompt_for("rag-v3") 반환값 + v3 언어 규칙 포함 검증. system_prompt_for_unknown_version_returns_err_with_hint (pipeline.rs:2314-2318) 의 bail! 메시지를 "expected rag-v1, rag-v2 or rag-v3" 로 갱신 + assert 보강 (contains 검사라 기존도 통과하나 명시). config default 테스트는 §6 (M2) 참조.
Todo #4 — doc.lang semantic (documentation only) [P0]
Problem: "감지 실패"가 아니라 설계상 의도. 모든 code parser (crates/kebab-parse-code/src/{rust,python,java,kotlin,cpp,c,go,javascript,typescript}.rs) 가 lang: Lang("und") 하드코딩. code 는 자연어 감지를 안 함. 대신 code_lang (crates/kebab-core/src/metadata.rs:38) 가 소스 언어 보유. 자연어 감지 (lingua) 는 Markdown 등 prose parser (crates/kebab-parse-md/src/frontmatter.rs:557 detect_lang) 에서만 수행. und 53% = code 비중.
Decision: 자연어로 명확화 + 문서화. 코드 변경 없음 (user confirmation 완료).
Implementation:
-
wire schema 명확화 (
docs/wire-schema/v1/schema.schema.json또는 README):lang(자연 언어 prose, lingua 감지) vscode_lang(소스 코드 언어, ast parser) 의미 분리 명시.- "und" 가 code file 에서 정상 (code parser 가 자연 언어 감지 안 함) 임을 명시.
lang_breakdown의 und 가 대부분 code 비중임을 추가.
-
README 갱신 (해당 섹션):
- 같은 명확화 추가.
Non-goal: code 주석 자연 언어 감지 (Option B 기각 — V010 migration + 전체 재처리 비용).
Todo #2 — bulk search input format (docs + 소량 code) [P0]
Problem: 사용자가 여러 input shape ({"text":...}, {"query":{"text":...}}) 를 시도했으나 모두 실패. 정확한 shape 가 docs 에 부재.
Actual shape (leader 가 crates/kebab-app/src/bulk.rs:124-184 parse_one 직접 검증):
{"query": "<텍스트>", "mode": "lexical|vector|hybrid", "k": 3,
"trust_min": "primary|secondary|generated", "ingested_after": "<RFC3339>",
"media": ["<media>"], "tag": ["<tag>"], "lang": "<iso>"}
query(required, string — nested object 아님).mode(optional, defaulthybrid; lexical/vector/hybrid).k(optional, int; 생략 또는 0 → app 이 configsearch.default_k(현재 10,kebab-config/src/lib.rs:697)로 해석. wire default 는 0,bulk.rs:140-143). (m1)trust_min(optional, enum primary/secondary/generated).ingested_after(optional, RFC3339 date-time).media(optional, array of string — alias 정규화).tag(optional, array of string).lang(optional, string ISO code).
Implementation:
-
wire schema 추가 (
docs/wire-schema/v1/bulk_search_input.schema.json신규):- 위 8개 필드 명시 (required:
query만; 나머지 optional + default 설명). - (m4) 기존
bulk_search_item.schema.json의query필드(= "Input echo, verbatim JSON object") description 을 신규 input schema 로$ref/ cross-ref 연결해 일관성 유지.
- 위 8개 필드 명시 (required:
-
CLI help 갱신 (
kebab search --bulk --help):echo '{"query":"한국","mode":"lexical","k":3}' | kebab search --bulk --json -
error message 갱신 (
bulk.rs:129):- 현재:
missing required field: query - 개선: expected shape hint 추가 (예:
missing required field: query (expected {"query":"<text>","mode":"lexical|vector|hybrid",...})).
- 현재:
-
docs/DOGFOOD.md 갱신: bulk scenario 예시 정정.
Todo #3 — list docs title 중복 (code) [P0]
Problem: heading 기반 title 추출이 unique 하지 않아 여러 file 이 title: "Registry", title: "dispatch" 등 동일 title 로 반환.
Decision (self-review): Option A — human-readable 출력에 doc_path 보조 표시. title 자체·wire schema 는 불변 (안전).
Implementation:
-
human-readable output (
crates/kebab-cli/src/main.rs또는 등가, list docs 서브커맨드):- 각 doc row 에
doc_path컬럼 추가 (현재--json에는 이미 노출). - 예:
title: "Registry" doc_path: "src/Registry.java"
- 각 doc row 에
-
README
kebab list docs동작 명세 갱신: path 표시 추가 명시.
Non-goal: title unique 화 (filename 추가 suffix 등) — title 자체는 wire schema 로 exposed 되므로 변경 최소화.
Todo #5 / #6 — fusion_score / score_kind docs (docs) [P1]
Actual wire schema (docs/wire-schema/v1/search_hit.schema.json):
- required:
score(top-level),score_kind,retrieval(object),index_version. fusion_score,lexical_score,vector_score,lexical_rank,vector_rank는 모두retrievalobject 내부.
top-level score 의 의미 (B1 — 코드 검증 완료, kebab-core/src/search.rs:95-99):
- top-level
score는 canonical ranking score 이며, 그 의미를score_kind가 선언한다:Rrf(hybrid) — RRF normalized[0,1]Bm25(lexical-only) — raw BM25Cosine(vector-only) — raw cosine
- single-mode 검색에서는 fusion 미실행 →
score == retrieval.fusion_score. hybrid 에서만retrieval.fusion_score가 RRF normalized 값.
Finding X (score_kind semantics): lexical mode 의 score == fusion_score == lexical_score 는 위 single-mode 동작의 정상 귀결.
Note (m2): score_kind 의 rrf/bm25/cosine 의미는 이미 search_hit.schema.json 의 score_kind description (p9-fb-38) 에 문서화돼 있음 → 실제 gap 은 README + (score ↔ fusion_score 관계). 범위를 "README 보강 + schema 의 기존 score_kind 설명 cross-ref" 로 한정 (schema 자체 변경 최소).
Implementation:
-
README search response 예시 정정:
.retrieval.{fusion_score, lexical_score, vector_score, lexical_rank, vector_rank}구조 + top-levelscorevsretrieval.fusion_score관계 (위 B1) 명시. -
schema.json description: README 에서 기존
score_kinddescription cross-ref. (schema 자체는 충분 — 필요 시 description 보강만.)
Todo #7 — schema --json index_version (docs) [P1]
Problem: crates/kebab-app/src/schema.rs:213 의 index_version 은 vector store (lance) index version. search hit 의 index_version 은 lexical (fts5-v009-...). 두 의미 다름.
Decision (self-review): rename 안 함 (wire v1 호환성 보존). 문서화만.
Implementation:
- README +
docs/wire-schema/v1/schema.schema.jsondescription:- schema.json 의
index_version= vector store (lance) index version. 예:"v1". - search_hit.json 의
index_version= lexical (FTS5) index version. 예:"fts5-v009-korean-morphological". - 두 field 의 의미 다르며, version cascade 에서 별도 추적됨 명시.
- schema.json 의
Todo #8 — Ollama endpoint default (docs) [P2]
Problem: localhost default 유지하나, remote Ollama 사용 시 setup 단계에서 endpoint 갱신 필요성 안내 부재.
Decision (self-review): localhost default 유지 + init hint.
Implementation:
kebab init의 hint 메시지 (에 따라 코드 수정):- "Remote Ollama 사용 시 [config 파일] 의
[models.llm] endpoint갱신 필요" 안내.
- "Remote Ollama 사용 시 [config 파일] 의
5. Contract / Version / Release 영향
5.1 Frozen Contract Bump
docs/superpowers/specs/2026-04-27-kebab-final-form-design.md 갱신 (same PR). load-bearing default 선언은 line 1349 + 1533 (★):
- ★ line 1349 (config 예시):
prompt_template_version = "rag-v2" # default→"rag-v3". - ★ line 1533 (§9 cascade table):
prompt_template_version행의 "코드 상수 (rag-v2)" → rag-v3. - line 899 (m3): 기존 텍스트는 "V1 은 legacy backwards-compat 으로 보존" (rag-v1 에 관한 것). 변경 후 v1·v2 둘 다 legacy (v3 default) 이므로 이 노트에 rag-v2 도 legacy 임을 추가.
- line 287 (n1):
answer.v1JSON 예시 블록 안 stale 값 (같은 블록 model 이qwen2.5:14b-instruct로 현행 gemma4 와 이미 불일치). historical 예시로 두거나 블록 전체 refresh — 단독 교체는 비일관 예시 유발. default 선언 대상 아님 (1349/1533 과 동급으로 묶지 말 것).
(contract 규칙) CLAUDE.md "design doc 변경 → 모든 referencing task spec 같은 PR": prompt_template_version / §9 를 참조하는 tasks/p<N>/ task spec 이 있으면 같은 PR 에서 동반 갱신 (구현 단계에서 grep 확인).
5.2 Version Cascade
- workspace
Cargo.tomlversion bump: 0.20.1 → 0.20.2 (patch, release commit). - wire schema: additive only (bulk_search_input schema 추가, description 갱신). breaking 아님 → v1 유지.
- config schema: no breaking changes.
prompt_template_versiondefault 변경은 user config 에서 명시 시 legacy 유지. - eval cascade (M4):
prompt_template_version은 §9 cascade 의 snapshot 대상 — eval runner 가eval_runs.config_snapshot_json에 박제. rag-v3 default flip 후 신규 eval run 의config_snapshot_json.prompt_template_version이rag-v3로 기록됨 → rag-v2 baseline 과compare시 prompt 차원 변경을 감안해야 함. 코드 변경 / migration 불필요 (additive) — eval 비교 해석에만 영향. - dogfood trigger 해당: #1 (RAG prompt template 변경 = Search/RAG behavior trigger) → dogfood verification 필수.
5.3 Release Notes
docs/release-notes/v0.20.2-draft.md (또는 gitea release body):
- 변경 사실: RAG response language auto-matching (query 언어 → response 언어).
- Trade-off: 한국어 corpus 를 영어로 물으면 LLM 이 영어로 답함 (한국어 근거 번역). citation 은 유지.
- Dogfood evidence:
/build/dogfood/kb/에서 영어/한국어/혼합 query 응답 언어 검증 결과 (hyperlink to HOTFIXES). - Documentation 정확화: wire schema (fusion_score, score_kind, index_version), bulk search input, list docs path, doc.lang semantic, Ollama endpoint hint.
6. Testing / Dogfood 검증 정책
Per CLAUDE.md § 도그푸딩 검증 정책:
-
Full dogfood 재실행 (각 finding 구현 후):
- re-ingest: prompt/docs 변경은 chunk 불변 → ingest skip. #1/#4/docs todo 는 재-ingest 불필요.
docs/DOGFOOD.md§1~§11 query 시나리오 + handoff §3 regression 시나리오 전체 실행.- dogfood KB:
/build/dogfood/kb/, config:/build/dogfood/config.toml, binary = 새로 빌드한 release.
-
각 finding 별 검증 체크리스트:
- #1: 영어/한국어/혼합 query 의 응답 언어 일치 확인.
- #2: bulk search
{"query":"테스트","mode":"lexical","k":3}정상 동작. - #3:
kebab list docs출력에 title + path 함께 표시. - #4: schema 및 README 문서화 검토 (code 동작 확인 불필요).
- #5/#6: search response 예시 정정 및 schema description 정확성 검토.
- #7: schema --json 및 search hit 의
index_version의미 documentation 검토. - #8:
kebab init출력에 Ollama endpoint hint 노출 확인.
-
Unit test:
system_prompt_for("rag-v3")반환값 + v3 언어 규칙 포함.- unknown 에러 메시지 갱신 ("expected rag-v1, rag-v2 or rag-v3") —
pipeline.rs:2314-2318. - (M2) config default 테스트 갱신:
defaults_rag_prompt_template_version_is_rag_v2(kebab-config/src/lib.rs:1316) →..._is_rag_v3, assert"rag-v3". default flip 시 이 테스트가 FAIL 하므로 반드시 동반 수정. (pipeline.rs:2463fixture 는 명시값"rag-v2"라 영향 없으나 확인.)
-
도그푸딩 결과 기록 (
tasks/HOTFIXES.md+ release notes):- dated entry: 각 finding 검증 scenario + evidence snippet.
- release notes: user-facing surface 변경 (RAG response language, docs 정확화).
7. Non-goals (Handoff §2.4 별도 spec)
--response-langflag (Todo #1 Option B 기각).- code 주석 자연 언어 감지 (Todo #4 Option B 기각).
- BS-A HTML 지원 / BS-B Tier visibility / BS-C
kebab dogfoodsubcommand / BS-D code chunk N-gram / BS-E builtin_blacklist 명세.
8. References
- Parent handoff:
docs/superpowers/handoffs/2026-05-28-v0.20.1-fulldogfood-findings-handoff.md - Frozen contract:
docs/superpowers/specs/2026-04-27-kebab-final-form-design.md - Dogfood run:
/build/dogfood/kb/, logs at/build/dogfood/logs/ - Wire schema:
docs/wire-schema/v1/ - Code locations:
- RAG:
crates/kebab-rag/src/pipeline.rs:1872~1883 - Metadata:
crates/kebab-core/src/metadata.rs:38 - Frontmatter:
crates/kebab-parse-md/src/frontmatter.rs:557 - Bulk search:
crates/kebab-app/src/bulk.rs:127, 293 - Schema:
crates/kebab-app/src/schema.rs:213 - Config:
crates/kebab-config/src/lib.rs
- RAG:
Spec Self-Review
round-1 critic 리뷰 (opus,
.omc/spec-review-r1.md) 반영 완료: B1(top-level score placeholder→확정), M1(caption out-of-scope), M2(config default 테스트), M3(bulk 전체 필드 + leader 가 tag/lang 추가 발견), M4(eval cascade), m1m5 / n1n2.
✅ Placeholder check: round-1 에서 발견된 line 189 placeholder ("top-level score 확인 후 명시") 를 확정 답 (score_kind 가 의미 선언, search.rs:95-99) 으로 교체. 잔여 "TBD"/"TODO" 없음.
✅ Internal contradiction: 각 todo 간 순서 + 의존성 명확. frozen contract 갱신은 모든 구현 후 한 커밋으로.
✅ Scope: patch release (v0.20.2) 로 수렴하는 8개 finding 만 포함. 새 기능 / 범위 확장 없음. 이미지 caption 언어 동작은 명시적 out-of-scope (M1).
✅ Ambiguity: 각 todo 의 contract_section 명시, code path 검증, 예시 포함. wire schema 는 v1 유지 (additive).
✅ Contract section mapping: §6 (config + prompt_template_version default), §9 (version cascade).
✅ Code path accuracy: 인용 line 번호를 opus critic 이 grep/read 로 round-1 재검증. bulk k default·bulk 추가 필드(tag/lang)·contract line 899/287 표현 정정 반영 (line 번호 정확성 ≠ 사실 검증 완결성 — round-1 에서 보강).
✅ Dogfood verification: CLAUDE.md § 도그푸딩 검증 정책 따라 각 finding 구현 후 full dogfood 재실행 계획 명시.