feat(p10-1a-1): code ingest framework — wire schema + parse-code crate + filter flags #139

Merged
altair823 merged 21 commits from spec/p10-code-ingest-design into main 2026-05-15 09:31:31 +00:00
Owner

Goal

Phase 1A-1 of the code ingest design (docs/superpowers/specs/2026-05-15-kebab-code-ingest-design.md). Land the framework surface for code ingest — wire schema (additive minor), CLI filter flags, ignore policy, skip policy infrastructure, kebab-parse-code crate skeleton, [ingest.code] config section — without enabling any code chunker. 1A-2 plugs in the Rust AST chunker on top.

What's in

  • 도메인 타입: 새 Citation::Code variant, SearchHit.repo / code_lang (optional), IngestReport 5 skip counters + SkipExamples, Metadata 4 신규 필드 (repo / git_branch / git_commit / code_lang).
  • 새 crate kebab-parse-code: lang::code_lang_for_path (extension → 식별자), repo::detect_repo (gix walk-up), skip::is_generated_file / is_oversized / BUILTIN_BLACKLIST (6 entry safety net).
  • Walker 통합: .gitignore honor + built-in blacklist + 카테고리별 skip 카운터 분류. 우선순위: built-in > .gitignore > .kebabignore.
  • Pre-parse skip: generated header sniff (@generated, DO NOT EDIT 등 7 markers, case-insensitive) + size cap (max_file_bytes / max_file_lines).
  • Config: 신규 [ingest.code] 절 (7 필드, 모두 default).
  • CLI: --repo, --code-lang, --media code filter (1A-1 시점에는 hit 0).
  • Wire schema v1: citation / search_hit / ingest_report / schema 4 파일에 additive minor.
  • Docs: frozen design §0 / §2 / §3 / §8 / §11 갱신, README / HANDOFF / SMOKE 갱신, tasks/p10/ 신설.

What's NOT in

  • 실제 코드 chunker (1A-2 부터).
  • 어떤 코드 file 도 indexed 안 됨 (code Citation variant 는 정의됐지만 아무도 생성하지 않음).
  • multi-workspace / watch mode / git history-aware (frozen design §11 그대로 비-스코프).

Verification

  • cargo test --workspace --no-fail-fast -j 1: 685 passed, 0 failed, 28 ignored.
  • cargo clippy --workspace --all-targets -- -D warnings: clean.
  • Smoke ingest (kebab init + kebab ingest --json): 새 5 skip counter + skip_examples 모두 surface, 기존 wire shape (scanned / new / skipped / ...) 무영향.
  • Markdown SearchHit 의 JSON output: repo / code_lang 정상 omit.
  • kebab schema --json: code_lang_breakdown / repo_breakdown 빈 map.

Implementation note

Plan (docs/superpowers/plans/2026-05-15-p10-1a-1-code-ingest-framework.md) 의 21 task 를 subagent-driven 으로 실행. 각 task 가 implementer + spec reviewer + code quality reviewer 3-stage. 도중 follow-up fix 5건 (test-only SearchHit construction sites missed by Task 2, kebab-parse-code manifest fields, .gitignore negation guard, Task 11 review cleanup, Task 16 wire.rs Stats fixture).

  • Design spec: docs/superpowers/specs/2026-05-15-kebab-code-ingest-design.md
  • Implementation plan: docs/superpowers/plans/2026-05-15-p10-1a-1-code-ingest-framework.md
  • Task spec: tasks/p10/p10-1a-1-code-ingest-framework.md
  • Phase 10 index: tasks/p10/INDEX.md
## Goal Phase 1A-1 of the code ingest design (`docs/superpowers/specs/2026-05-15-kebab-code-ingest-design.md`). Land the *framework surface* for code ingest — wire schema (additive minor), CLI filter flags, ignore policy, skip policy infrastructure, `kebab-parse-code` crate skeleton, `[ingest.code]` config section — without enabling any code chunker. **1A-2 plugs in the Rust AST chunker on top.** ## What's in - **도메인 타입**: 새 `Citation::Code` variant, `SearchHit.repo` / `code_lang` (optional), `IngestReport` 5 skip counters + `SkipExamples`, `Metadata` 4 신규 필드 (repo / git_branch / git_commit / code_lang). - **새 crate `kebab-parse-code`**: `lang::code_lang_for_path` (extension → 식별자), `repo::detect_repo` (gix walk-up), `skip::is_generated_file` / `is_oversized` / `BUILTIN_BLACKLIST` (6 entry safety net). - **Walker 통합**: `.gitignore` honor + built-in blacklist + 카테고리별 skip 카운터 분류. 우선순위: built-in > .gitignore > .kebabignore. - **Pre-parse skip**: generated header sniff (`@generated`, `DO NOT EDIT` 등 7 markers, case-insensitive) + size cap (`max_file_bytes` / `max_file_lines`). - **Config**: 신규 `[ingest.code]` 절 (7 필드, 모두 default). - **CLI**: `--repo`, `--code-lang`, `--media code` filter (1A-1 시점에는 hit 0). - **Wire schema v1**: citation / search_hit / ingest_report / schema 4 파일에 additive minor. - **Docs**: frozen design §0 / §2 / §3 / §8 / §11 갱신, README / HANDOFF / SMOKE 갱신, `tasks/p10/` 신설. ## What's NOT in - 실제 코드 chunker (1A-2 부터). - 어떤 코드 file 도 indexed 안 됨 (`code` Citation variant 는 정의됐지만 아무도 생성하지 않음). - multi-workspace / watch mode / git history-aware (frozen design §11 그대로 비-스코프). ## Verification - `cargo test --workspace --no-fail-fast -j 1`: 685 passed, 0 failed, 28 ignored. - `cargo clippy --workspace --all-targets -- -D warnings`: clean. - Smoke ingest (`kebab init` + `kebab ingest --json`): 새 5 skip counter + skip_examples 모두 surface, 기존 wire shape (scanned / new / skipped / ...) 무영향. - Markdown SearchHit 의 JSON output: `repo` / `code_lang` 정상 omit. - `kebab schema --json`: `code_lang_breakdown` / `repo_breakdown` 빈 map. ## Implementation note Plan (`docs/superpowers/plans/2026-05-15-p10-1a-1-code-ingest-framework.md`) 의 21 task 를 subagent-driven 으로 실행. 각 task 가 implementer + spec reviewer + code quality reviewer 3-stage. 도중 follow-up fix 5건 (test-only SearchHit construction sites missed by Task 2, kebab-parse-code manifest fields, .gitignore negation guard, Task 11 review cleanup, Task 16 wire.rs Stats fixture). ## Links - Design spec: `docs/superpowers/specs/2026-05-15-kebab-code-ingest-design.md` - Implementation plan: `docs/superpowers/plans/2026-05-15-p10-1a-1-code-ingest-framework.md` - Task spec: `tasks/p10/p10-1a-1-code-ingest-framework.md` - Phase 10 index: `tasks/p10/INDEX.md`
altair823 added 20 commits 2026-05-15 09:18:47 +00:00
수십 개 git repo (한 부모 dir 아래) 를 corpus 로 확장. Tier 1 (Rust/Python/TS-JS/Go/Java/Kotlin/C/C++) 은 tree-sitter AST per-language chunker, Tier 2 (k8s manifest / Dockerfile / Cargo.toml 류) 는 resource-aware chunker, Tier 3 (shell / fallback) 는 paragraph + line-window. embedding 은 multilingual-e5-large 유지 — cross-corpus 검색 위해. Phase 1A (Rust) 부터 1D (C/C++) + Phase 2 (Tier 2) + Phase 3 (Tier 3) 순으로 진행. ignore 통합 (.gitignore honor + .kebabignore 추가 + 최소 built-in safety net), generated header sniff, size cap 으로 첫 도그푸딩 비용 차단. 새 Citation variant `code`, SearchHit 의 repo/code_lang 필드, --media code / --code-lang / --repo filter — 모두 additive minor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1A 가 들고 들어가는 *프레임워크 surface* (Citation `code` variant, SearchHit repo/code_lang, --media code / --code-lang / --repo filter, skip 정책, IngestReport 세분화, config 절, kebab-parse-code crate skeleton) 가 *언어 chunker 자체* 와 독립 검증 가능 — 1A-1 머지 후 기존 markdown corpus 의 wire 출력이 byte-level identical 한지 regression test 로 검증한 다음 1A-2 에서 Rust AST chunker 자체에 집중. binary version bump 트리거도 1A-2 로 미룸 (1A-1 은 wire additive minor + 사용자 surface 변경 없음).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
21 task plan: kebab-core 도메인 타입 (Citation::Code variant, SearchHit repo/code_lang, IngestReport skip counters, Metadata extension), 새 kebab-parse-code crate (lang/repo/skip 모듈, gix dep), kebab-source-fs gitignore+blacklist 통합, kebab-config [ingest.code] 절, kebab-cli --repo/--code-lang flag, wire schema JSON 갱신, frozen design doc 갱신, README/HANDOFF/SMOKE 갱신, task index. 각 task 가 5-step TDD cycle (test fail → impl → pass → commit). 코드 chunker 는 1A-1 에 없음 — 1A-2 에서 추가.

spec 의 Citation::Code 예시가 기존 5 variants 의 flat wire 형태와 안 맞아서 (`code: {...}` 중첩이 아니라 top-level field) 같이 fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wire two new optional fields onto SearchHit (skip_serializing_if = None)
and two Vec<String> filter fields onto SearchFilters (serde default).
Add RetrievalDetail::Default impl (manual, uses SearchMode::Hybrid as
sentinel). Patch all downstream SearchHit / SearchFilters literal
constructors with repo: None / code_lang: None / vec![] as appropriate.
Also covers Citation::Code arm in kebab-eval metrics match.
Add repo: None, code_lang: None to the 3 SearchHit struct literals
inside #[cfg(test)] blocks that were missed by the fa4eeb5 sweep.
Adds five new u32 counters (skipped_gitignore, skipped_kebabignore,
skipped_builtin_blacklist, skipped_generated, skipped_size_exceeded)
and a SkipExamples struct (≤5 sample paths per category) to
IngestReport. All new fields are #[serde(default)] for backward-compat
deserialization. Downstream literal construction sites patched with
zeros/empty; snapshot re-baked.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Four optional, serde-skipped-when-None fields added to `Metadata` for
code ingest context. All 11 downstream construction sites patched with
`repo: None, git_branch: None, git_commit: None, code_lang: None`.
Full workspace check (`--tests`) and per-crate test suite pass clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tasks 5-8: new `kebab-parse-code` crate with three infrastructure modules
for the code ingest framework. Ships lang.rs (extension→language identifier
mapping), repo.rs (.git walk-up via gix 0.70 for RepoMeta), and skip.rs
(BUILTIN_BLACKLIST, is_generated_file, is_oversized). 14 integration tests
across three test files, all passing; clippy -D warnings clean.

Note: gix pinned to 0.70 (not 0.83 as originally suggested) because 0.83
fails to compile against Rust 1.94.1 due to non-exhaustive match patterns
in gix-hash. 0.70 resolves cleanly and has identical head_name/head_id API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wires `kebab_parse_code::BUILTIN_BLACKLIST` (6 patterns: node_modules,
target, __pycache__, .venv, venv, env) into `build_overrides()` so the
walker automatically excludes these directories even when the user has
no `.kebabignore`. TDD cycle: 2 failing tests added first, then the
pattern-add loop inserted after the existing kbignore block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds read_gitignore() (pub(crate), root-only, nested cascade deferred)
and merges its patterns as a 5th group in build_overrides(). Trailing-
slash patterns (dist/) are normalized to also emit a stem/** glob so
files inside the directory are matched when is_dir=false. Two new tests
cover both the happy path and the missing-file no-op.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevent double-`!` corruption when a `.gitignore` negation pattern
(e.g. `!keep/`) hits the trailing-slash normalizer in `read_gitignore`.
Also updates module-level and `build_overrides` doc to list all five
filter sources in application order, and adds a regression test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactor walker to expose WalkOverrides (combined + per-source matchers),
add walk_files_with_skips that returns accepted files alongside skip
attribution, wire FsSourceConnector::scan_with_skips into kebab-app so
IngestReport.skipped_gitignore, skipped_kebabignore, skipped_builtin_blacklist,
and skip_examples are populated instead of left at zero. Priority order
per spec §5.2 (builtin > gitignore > kebabignore) enforced in classify_skip,
with a directory-aware builtin matcher so pruned directory entries are
correctly attributed to builtin rather than a coincident gitignore entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add IngestCfg + IngestCodeCfg structs with serde defaults and embed
ingest: IngestCfg into the top-level Config. Existing configs without
an [ingest] section continue to load unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wire kebab_parse_code::is_generated_file and is_oversized into
FsSourceConnector::scan_with_skips. Files that pass gitignore/builtin/
kebabignore matching are now checked for generated-file markers
(config-gated via ingest.code.skip_generated_header) and byte/line caps
(ingest.code.max_file_bytes / max_file_lines). FsScanSkips gains
skipped_generated + skipped_size_exceeded counters; kebab-app threads
them into IngestReport. Also fixes a pre-existing clippy::derivable_impls
warning in IngestCfg. Three new connector tests cover all three paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task 13: add wire regression tests proving markdown SearchHit omits
repo/code_lang when None, and all 5 original Citation variants serialize
byte-identically without spurious Code-variant keys.

Task 15: add --repo (repeatable) and --code-lang (repeatable,
comma-separated) flags to `kebab search`; propagate both into
SearchFilters instead of the previous vec![] stub. Add
#[allow(clippy::large_enum_variant)] — Cmd is short-lived, boxing buys
nothing.

Task 16: add code_lang_breakdown and repo_breakdown BTreeMap fields to
Stats (schema.v1); derive Default on Stats; populate both as empty in
collect_stats (1A-2 fills them when code chunks land). Add unit test
asserting both keys are present in the serialized object.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task 16's new code_lang_breakdown / repo_breakdown fields broke the existing schema_wrapper_tags_schema_version test in wire.rs which constructs Stats { ... } literally. Use ..Default::default() since Stats now derives Default.

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

회차 1 — 21 task 모두 per-task review 통과한 상태로 PR 등록됐는데, 전체 diff 다시 훑어보니 doc 레이어에서 4 건 actionable 발견. ���두 doc 일관성 / accuracy 이슈로 코드 자체는 무관.

주요 이슈:

  • frozen design §2.1 의 code variant 예시에 repo 필드가 들어가 있는데 Citation::Code 에 그런 필드 없음 (actual enum: path / line_start / line_end / symbol / lang). 그리고 같은 PR 안의 2026-05-15 spec §3.1 (commit 005a901) 은 flat form 으로 적었는데 2026-04-27 frozen design 의 같은 variant 는 nested form 이라 두 spec 이 서로 모순.
  • §2.2 SearchHit 예시에서 repo: null, code_lang: null 을 적어놓고 주석에 "omitted when null" — 모순 (실제 wire 는 키 자체가 빠짐).
  • HANDOFF Phase row 식별자 **10** (다른 row 는 **P0**/**P5** 식).
  • README synopsis 에 [--media code] 가 별도 flag 처럼 두 번 나타남.

코드 자체 / 테스트 / wire schema JSON / chunker 흐름은 모두 통과. 다음 회차에서 위 4건 후속 commit 으로 정리.

회차 1 — 21 task 모두 per-task review 통과한 상태로 PR 등록됐는데, 전체 diff 다시 훑어보니 doc 레이어에서 4 건 actionable 발견. ���두 doc 일관성 / accuracy 이슈로 코드 자체는 무관. 주요 이슈: - **frozen design §2.1 의 code variant 예시에 `repo` 필드가 들어가 있는데 `Citation::Code` 에 그런 필드 없음** (actual enum: path / line_start / line_end / symbol / lang). 그리고 같은 PR 안의 2026-05-15 spec §3.1 (commit 005a901) 은 flat form 으로 적었는데 2026-04-27 frozen design 의 같은 variant 는 nested form 이라 두 spec 이 서로 모순. - §2.2 SearchHit 예시에서 `repo: null, code_lang: null` 을 적어놓고 주석에 "omitted when null" — 모순 (실제 wire 는 키 자체가 빠짐). - HANDOFF Phase row 식별자 `**10**` (다른 row 는 `**P0**`/`**P5**` 식). - README synopsis 에 `[--media code]` 가 별도 flag 처럼 두 번 나타남. 코드 자체 / 테스트 / wire schema JSON / chunker 흐름은 모두 통과. 다음 회차에서 위 4건 후속 commit 으로 정리.
HANDOFF.md Outdated
@@ -20,6 +20,7 @@ P0P5 + P6 + P7 + P9-1/2/3/4 (Library / Search / Ask / Inspect) 머지 완료.
| **P7** | PDF text + page citation | `kebab-parse-pdf` | P5 | ✅ 완료 (3/3 component, page-level chunker + ingest wiring) |
| **P8** | 음성 transcription + timestamp citation | `kebab-parse-audio` | P5 | ⏸ 보류 (whisper-rs 시스템 dep brainstorm 필요) |
| **P9** | TUI + desktop app | `kebab-tui`, `kebab-desktop` | P5 | 🟡 진행 (4/5 component — P9-1/2/3/4 완료 [Library / Search / Ask / Inspect], P9-5 desktop 예정 · 도그푸딩 피드백 **20/20 ✅**) |
| **10** | code ingest framework | `kebab-parse-code` | P5 | 🟡 진행 중 (1A-1 머지 직전) — 1A-1 머지 시점 wire schema additive minor + 새 crate kebab-parse-code skeleton 동결, 실제 code chunker 는 1A-2 부터 |

Phase 식별자가 **10** — 다른 row 는 **P0** / **P5** / **P9** 처럼 P prefix. 일관성 위해 **P10** 으로.

Phase 식별자가 `**10**` — 다른 row 는 `**P0**` / `**P5**` / `**P9**` 처럼 `P` prefix. 일관성 위해 `**P10**` 으로.
README.md Outdated
@@ -72,3 +72,3 @@
| `kebab init` | XDG 경로에 데이터 디렉토리 + config.toml 생성 |
| `kebab ingest [<path>]` | Markdown / 이미지 / PDF 색인 (idempotent). TTY 에서는 stderr 진행 바, non-TTY (CI / pipe) 는 stderr 한 줄씩, `--json` 은 stdout 에 `ingest_progress.v1` 라인 streaming 후 마지막에 `ingest_report.v1`. Ctrl-C 한 번이면 현재 asset 마무리 후 abort (부분 commit 보존, idempotent re-run), 두 번째 Ctrl-C 는 hard exit. Markdown title 이 frontmatter 에 없어도 첫 H1 → H2 → 첫 paragraph 80 자 → 파일명 순으로 자동 채움 (parser_version `md-frontmatter-v2`) — 기존 색인된 doc 도 다음 ingest 에서 새 title 로 갱신. **Incremental** (p9-fb-23): 두 번째 이후의 ingest 는 변하지 않은 doc (blake3 + parser/chunker/embedder version 모두 동일) 의 parse/chunk/embed/vector upsert 를 자동 스킵. final summary 에 `N unchanged` 카운트 표시. `--force-reingest` 로 skip 무시 강제 재처리. **지원 형식** (extractor 자동 결정 — config 에 명시 불가): Markdown (`.md`), 이미지 (`.png` / `.jpg` / `.jpeg`, OCR + caption), PDF (`.pdf`). 다른 확장자는 자동 skip — `IngestItem.warnings` 에 사유 (`"unsupported media type: .docx"` 등), `IngestReport.skipped_by_extension` 에 카운트 분류, CLI / TUI summary 에 breakdown 표시. |
| `kebab search --mode {lexical,vector,hybrid} "<query>" [--no-cache] [--max-tokens N] [--snippet-chars N] [--cursor <opaque>] [--tag T] [--lang L] [--path-glob G] [--trust-min LEVEL] [--media TYPE] [--ingested-after RFC3339] [--doc-id ID] [--trace] [--bulk]` | 검색. hybrid는 RRF fusion, citation 포함. 같은 process 안에서 동일 query (NFKC + trim + lowercase 정규화) 반복 시 in-process LRU 캐시 hit (capacity = `[search] cache_capacity`, default 256). `--no-cache` 로 강제 bypass — 디버깅용. ingest commit 발생 시 `kv['corpus_revision']` bump 으로 모든 entry 자동 stale. **`--max-tokens` / `--snippet-chars` / `--cursor` (p9-fb-34)** — agent budget controls. `--json` 출력은 `search_response.v1` wrapper (`{hits, next_cursor, truncated}`) — pre-fb-34 의 bare array 와 호환 안 됨. mismatched cursor → `error.v1.code = stale_cursor`. **filter flags (p9-fb-36):** `--tag` 는 반복 가능 flag (`--tag rust --tag async`) 로 OR 매칭, `--media``,` 구분 다중 값 OR 매칭, 나머지 flags 간은 AND 조합. `--trust-min``primary\|secondary\|generated` 중 하나 (해당 level 이상 포함). `--ingested-after` 는 RFC3339 UTC — 파싱 실패 시 `error.v1.code = config_invalid` (exit 2). `--media md``markdown` alias 로 정규화. 알 수 없는 `--media` 값은 무조건 empty hits (오류 아님). **`--trace` (p9-fb-37)** — `search_response.v1.trace` 에 lexical / vector pre-fusion 후보 + RRF union + per-stage timing (`lexical_ms` / `vector_ms` / `fusion_ms` / `total_ms`) 노출. trace 요청은 캐시 우회 (`--no-cache` 없이도 항상 cold). **`--bulk` (p9-fb-42)** — stdin ndjson 으로 N query 한 번에 실행. `--json` 면 stdout per-query ndjson (`bulk_search_item.v1`) + stderr summary (`bulk_summary: total=N succeeded=S failed=F`). Cap 100. agent 가 query decomposition 후 sub-query 일괄 실행 시 single round-trip — App instance 재사용으로 캐시 / embedder cold-start 비용 한 번만. Per-query failure 는 item 의 `error` (error.v1) 에 격리, 다른 query 계속 진행. |
| `kebab search --mode {lexical,vector,hybrid} "<query>" [--no-cache] [--max-tokens N] [--snippet-chars N] [--cursor <opaque>] [--tag T] [--lang L] [--path-glob G] [--trust-min LEVEL] [--media TYPE] [--ingested-after RFC3339] [--doc-id ID] [--trace] [--bulk] [--repo NAME ...] [--code-lang LIST] [--media code]` | 검색. hybrid는 RRF fusion, citation 포함. 같은 process 안에서 동일 query (NFKC + trim + lowercase 정규화) 반복 시 in-process LRU 캐시 hit (capacity = `[search] cache_capacity`, default 256). `--no-cache` 로 강제 bypass — 디버깅용. ingest commit 발생 시 `kv['corpus_revision']` bump 으로 모든 entry 자동 stale. **`--max-tokens` / `--snippet-chars` / `--cursor` (p9-fb-34)** — agent budget controls. `--json` 출력은 `search_response.v1` wrapper (`{hits, next_cursor, truncated}`) — pre-fb-34 의 bare array 와 호환 안 됨. mismatched cursor → `error.v1.code = stale_cursor`. **filter flags (p9-fb-36):** `--tag` 는 반복 가능 flag (`--tag rust --tag async`) 로 OR 매칭, `--media``,` 구분 다중 값 OR 매칭, 나머지 flags 간은 AND 조합. `--trust-min``primary\|secondary\|generated` 중 하나 (해당 level 이상 포함). `--ingested-after` 는 RFC3339 UTC — 파싱 실패 시 `error.v1.code = config_invalid` (exit 2). `--media md``markdown` alias 로 정규화. 알 수 없는 `--media` 값은 무조건 empty hits (오류 아님). **`--trace` (p9-fb-37)** — `search_response.v1.trace` 에 lexical / vector pre-fusion 후보 + RRF union + per-stage timing (`lexical_ms` / `vector_ms` / `fusion_ms` / `total_ms`) 노출. trace 요청은 캐시 우회 (`--no-cache` 없이도 항상 cold). **`--bulk` (p9-fb-42)** — stdin ndjson 으로 N query 한 번에 실행. `--json` 면 stdout per-query ndjson (`bulk_search_item.v1`) + stderr summary (`bulk_summary: total=N succeeded=S failed=F`). Cap 100. agent 가 query decomposition 후 sub-query 일괄 실행 시 single round-trip — App instance 재사용으로 캐시 / embedder cold-start 비용 한 번만. Per-query failure 는 item 의 `error` (error.v1) 에 격리, 다른 query 계속 진행. **code corpus filters (p10-1A-1):** `--repo` 는 반복 가능 (`--repo kebab --repo other`) OR 매칭. `--code-lang` 는 반복 또는 comma 다중 값 (`--code-lang rust,python`), 알 수 없는 값은 빈 hits. `--media code` 는 Tier 1/2/3 모든 code chunk 포함. 1A-1 시점에서는 indexed 된 code chunk 가 없어 filter 가 항상 빈 결과 — 1A-2 (Rust AST chunker) 머지 이후 실효. |

[--repo NAME ...] [--code-lang LIST] [--media code][--media code]별도 flag 가 아니라 기존 --media TYPE flag 의 한 가지 . 같은 synopsis 안에 [--media TYPE] 이 이미 있어서 두 번 등장하게 됨. 제거하고 뒤쪽 prose (--media code 는 Tier 1/2/3 모든 code chunk 포함) 만 두는 게 깔끔.

# 현재
[--media TYPE] [--ingested-after RFC3339] [--doc-id ID] [--trace] [--bulk] [--repo NAME ...] [--code-lang LIST] [--media code]

# 권장
[--media TYPE] [--ingested-after RFC3339] [--doc-id ID] [--trace] [--bulk] [--repo NAME ...] [--code-lang LIST]
`[--repo NAME ...] [--code-lang LIST] [--media code]` 중 `[--media code]` 는 *별도 flag* 가 아니라 기존 `--media TYPE` flag 의 한 가지 *값*. 같은 synopsis 안에 `[--media TYPE]` 이 이미 있어서 두 번 등장하게 됨. 제거하고 뒤쪽 prose (`--media code` 는 Tier 1/2/3 모든 code chunk 포함) 만 두는 게 깔끔. ``` # 현재 [--media TYPE] [--ingested-after RFC3339] [--doc-id ID] [--trace] [--bulk] [--repo NAME ...] [--code-lang LIST] [--media code] # 권장 [--media TYPE] [--ingested-after RFC3339] [--doc-id ID] [--trace] [--bulk] [--repo NAME ...] [--code-lang LIST] ```
@@ -183,2 +184,3 @@
"caption": { "model": "qwen2.5-vl:7b" },
"time": { "start_ms": 822000, "end_ms": 850000, "speaker": "S1" }
"time": { "start_ms": 822000, "end_ms": 850000, "speaker": "S1" },
"code": { "start": 10, "end": 42, "lang": "rust", "repo": "kebab", "symbol": "fn ingest" }

code variant 예시에 repo 필드가 있는데, 실제 Citation::Code enum 에는 repo 필드 없음 (crates/kebab-core/src/citation.rs 확인 — 필드 5개: path, line_start, line_end, symbol, lang). repoSearchHit / Metadata 에만 살고, Citation 에는 없음. 이 예시 그대로 두면 spec 가 actual wire 와 안 맞아서 향후 consumer 가 헷갈림.

start/end vs line_start/line_end 이슈도 있음 — 위에 있는 nested-form 그룹 (line: {start, end}, page: {page}, ...) convention 을 따른 거라면 OK 지만, 실제 Rust 코드는 flat enum (#[serde(tag = "kind")]) 이라 wire 출력에는 "line_start": 142, "line_end": 168 처럼 top-level. 2026-05-15 spec §3.1 (같은 PR 의 커밋 005a901) 에서는 flat 형태로 적었는데 frozen design 의 같은 항목은 nested 로 남아 있어서 두 spec 이 서로 다른 형태를 보여줌. 일관성 위해 정리하는 게 좋겠음.

**`code` variant 예시에 `repo` 필드가 있는데, 실제 `Citation::Code` enum 에는 repo 필드 없음** (`crates/kebab-core/src/citation.rs` 확인 — 필드 5개: `path`, `line_start`, `line_end`, `symbol`, `lang`). `repo` 는 `SearchHit` / `Metadata` 에만 살고, Citation 에는 없음. 이 예시 그대로 두면 spec 가 actual wire 와 안 맞아서 향후 consumer 가 헷갈림. 또 `start`/`end` vs `line_start`/`line_end` 이슈도 있음 — 위에 있는 nested-form 그룹 (line: {start, end}, page: {page}, ...) convention 을 따른 거라면 OK 지만, 실제 Rust 코드는 flat enum (#[serde(tag = "kind")]) 이라 wire 출력에는 `"line_start": 142, "line_end": 168` 처럼 top-level. 2026-05-15 spec §3.1 (같은 PR 의 커밋 005a901) 에서는 flat 형태로 적었는데 frozen design 의 같은 항목은 nested 로 남아 있어서 두 spec 이 서로 다른 형태를 보여줌. 일관성 위해 정리하는 게 좋겠음.
@@ -215,2 +229,3 @@
"embedding_model": "multilingual-e5-large",
"chunker_version": "md-heading-v1"
"chunker_version": "md-heading-v1",
"repo": null, // p10-1A-1: optional, omitted when null (code corpus only)

주석 // p10-1A-1: optional, omitted when null 과 값 null 이 모순. 실제 SearchHit.repo / code_lang#[serde(default, skip_serializing_if = "Option::is_none")]None 일 때 키 자체가 JSON 에서 빠짐 (null 값으로 등장 안 함). 예시에 null 적어두면 reader 가 '아 null 로 들어오는구나' 라고 잘못 이해. 두 옵션:

  1. 키 자체를 예시에서 제거 + 짧은 주석 (p10-1A-1: 코드 hit 에서만 surface — markdown hit 에는 absent).
  2. 값을 "kebab" / "rust" 같은 sample 로 채우고 주석을 optional 로.
주석 `// p10-1A-1: optional, omitted when null` 과 값 `null` 이 모순. 실제 `SearchHit.repo` / `code_lang` 는 `#[serde(default, skip_serializing_if = "Option::is_none")]` 라 `None` 일 때 *키 자체가* JSON 에서 빠짐 (null 값으로 등장 안 함). 예시에 `null` 적어두면 reader 가 '아 null 로 들어오는구나' 라고 잘못 이해. 두 옵션: 1. 키 자체를 예시에서 제거 + 짧은 주석 (`p10-1A-1: 코드 hit 에서만 surface — markdown hit 에는 absent`). 2. 값을 `"kebab"` / `"rust"` 같은 sample 로 채우고 주석을 `optional` 로.
altair823 added 1 commit 2026-05-15 09:24:25 +00:00
회차 1 review 의 4 건 actionable 모두 반영:

1. frozen design §2.1 의 code variant 예시에서 존재하지 않는 `repo` 필드 제거 + nested form 에서 actual wire (flat) 형태로 정리. 5 variant 의 nested-form illustrative example 은 그대로 두고, code variant 만 별도 block 으로 분리해서 actual wire 와 1:1 매칭. 또 위쪽 6 variant nested-form group 에서도 'code' 행 삭제 (정확한 contract 는 별도 block 에 있음).
2. §2.2 SearchHit 예시의 `repo: null, code_lang: null` + 'omitted when null' 주석 모순 제거 — 키 자체를 빼고 inline 주석으로 'markdown hit 에는 absent, 코드 hit 에서만 surface' 설명.
3. HANDOFF Phase row 식별자 `**10**` → `**P10**` (다른 row 와 일관성).
4. README synopsis 의 중복 `[--media code]` 제거 (`--media` 는 이미 위쪽에 한 번 있음, code 는 값 중 하나라 prose 에서 설명).

코드 변경 없음 — 모두 markdown 문서.

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

회차 2 — 회차 1 의 4 건 actionable 모두 반영 확인 (7961f88):

  • frozen design §2.1: nested-form group 에서 code 항목 제거 + 구현 노트 (wire 실제 형태) para 추가 (모든 6 variant 의 nested-vs-flat 차이 설명) + code variant 별도 block 으로 flat 형태 정확히 명시 (path / line_start / line_end / symbol / lang, repo 없음). 2026-05-15 spec 과 일치.
  • §2.2 SearchHit: null 라인 두 개 제거, inline 주석 한 줄로 정정 ('markdown hit 에는 키 absent, code hit 에서만 surface').
  • HANDOFF: **10****P10** (다른 row 와 prefix 일관).
  • README synopsis: 중복 [--media code] 제거.

추가 actionable 없음. 코드 / wire schema / 테스트 모두 그대로. 머지 가능.

회차 2 — 회차 1 의 4 건 actionable 모두 반영 확인 (`7961f88`): - frozen design §2.1: nested-form group 에서 `code` 항목 제거 + `구현 노트 (wire 실제 형태)` para 추가 (모든 6 variant 의 nested-vs-flat 차이 설명) + code variant 별도 block 으로 flat 형태 정확히 명시 (path / line_start / line_end / symbol / lang, `repo` 없음). 2026-05-15 spec 과 일치. - §2.2 SearchHit: `null` 라인 두 개 제거, inline 주석 한 줄로 정정 ('markdown hit 에는 키 absent, code hit 에서만 surface'). - HANDOFF: `**10**` → `**P10**` (다른 row 와 prefix 일관). - README synopsis: 중복 `[--media code]` 제거. 추가 actionable 없음. 코드 / wire schema / 테스트 모두 그대로. 머지 가능.
altair823 merged commit 9362cd0aae into main 2026-05-15 09:31:31 +00:00
altair823 deleted branch spec/p10-code-ingest-design 2026-05-15 09:31:33 +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#139