3 Commits

Author SHA1 Message Date
5f3a37cafa feat(kebab-app): P7-3 PDF ingest wiring — kebab ingest 가 PDF 자산도 처리
P7-1 (`PdfTextExtractor`) + P7-2 (`PdfPageV1Chunker`) 의 라이브러리를
`kebab-app::ingest_with_config` 에 와이어링. `kebab-source-fs` 가 이미
`*.pdf` 를 `MediaType::Pdf` 로 분류하던 자산이 이제 검색 가능한 doc 으로
색인됨. P6-4 image wiring 패턴과 평행 — `ingest_one_asset` 에 `MediaType::Pdf`
arm 추가, 새 private fn `ingest_one_pdf_asset` 로 분기.

핵심 동작:
- per-medium chunker 선택: PDF 자산은 `PdfPageV1Chunker` 하드코딩 (compile-time
  match 기반). `config.chunking.chunker_version` 은 markdown 만 represent —
  PDF 는 항상 `pdf-page-v1`. HOTFIXES entry `2026-05-02 P7-3` 에 deviation 기록.
- encrypted PDF / corrupt PDF → `errors+=1` + P7-1 의 `qpdf --decrypt` hint
  를 `IngestItem.error` 에 verbatim 보존.
- 빈/scanned candidate 페이지 → 0 chunk, P7-1 의 `Provenance::Warning` 그대로
  통과. v1 에서는 검색 불가, P+ scanned-PDF OCR fallback 대기.
- determinism stress: extract → chunk 사이 `now()` 추가 호출 없음 (P6-4 invariant
  계승). PDF doc/chunk_id 모두 결정적.

통합 테스트 (`tests/pdf_pipeline.rs`, 8 passed + 1 ignored):
- 3-page text PDF → 1 doc + 3 chunk + Page span 검증
- identical re-ingest → Updated, doc_id 동일
- encrypted PDF → Error + `qpdf` hint 보존
- corrupt header PDF → Error + 미저장
- mixed page (page 2 빈) → 2 chunk + Warning 1개
- IngestReport 산술 invariant
- 50-page 긴 PDF → ≥50 chunk
- inspect doc → SourceSpan::Page round-trip
- (ignored) edited bytes re-ingest → storage UNIQUE bug 노출, P+ fix 대기

추가 발견 (HOTFIXES `2026-05-02 P7-3`): `assets.workspace_path` 의 UNIQUE
제약과 `upsert_asset_row` 의 `ON CONFLICT(asset_id)` 만 처리하는 부분 사이에
gap 존재. byte 변경 시 새 asset_id → 같은 workspace_path 충돌. md / image / pdf
모두 영향. P7-3 통합 테스트가 처음 노출. 본 PR 은 fix 안 함 — P+ storage task.

`docs/SMOKE.md` 에 PDF 섹션 + 검증 체크리스트 + 알려진 동작 4건 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 09:28:06 +00:00
a6e536d3c4 review(p7-3 spec): 회차 1 지적 반영
- RAG `kebab ask` 통합 테스트 → out of scope 로 이동. RAG citation 라운드트립
  검증은 P4-3 의 책임 영역이고 PDF chunk 도 Citation shape 동일. wiremock+RAG
  인프라를 P7-3 통합 테스트에 신설하는 비용이 본 task 의 invariant 와
  비례하지 않음.
- byte-edit re-ingest 케이스 추가. 동일 byte (P1 idempotency) 외에 byte
  수정 → 새 doc_id → `new+=1` 시나리오를 명시적으로 잠금.
- "Embedding call fails" 행을 `Embedder::embed Err → errors+=1, doc + chunks
  rows 는 이미 저장됨, 재실행 시 재시도" 로 명시. md path 코드 (lib.rs:621+)
  와 일치.
- Dispatch 절에 "이전엔 PDF 가 Skipped 였음 → 머지 후 skipped→scanned 이동"
  운영 jump 한 줄 추가.
- `PdfPageV1Chunker::chunk Err` 비고를 "P7-1 contract drift OR future
  routing bug — defensive validation either way" 로 정확화.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 09:11:17 +00:00
aebf900b2f docs(tasks): P7-3 pdf ingest wiring task spec
P7-1 (`PdfTextExtractor`) + P7-2 (`PdfPageV1Chunker`) 의 라이브러리는
완성됐지만 `kebab-app::ingest` 가 `MediaType::Pdf` 를 dispatch 하지 않아
CLI 에서 PDF 가 보이지 않는 상태. P7-3 이 그 와이어링을 다룬다 — P6-4
의 image wiring 패턴과 평행.

핵심 결정 (spec 본문):
- 새 private fn `ingest_one_pdf_asset` (P6-4 의 `ingest_one_image_asset`
  와 평행). `ingest_one_asset` match 에 `MediaType::Pdf` arm 추가.
- per-medium chunker 선택: PDF 는 `PdfPageV1Chunker` 하드코딩 (md 는
  `MdHeadingV1Chunker` 그대로). `config.chunking.chunker_version` 은 PDF
  ingest 에서 무시 (deviation, HOTFIXES 추가 예정).
- encrypted PDF / corrupt PDF → `errors+=1` + `IngestItem.error` 에 P7-1
  의 `qpdf --decrypt` 안내 그대로 보존.
- 빈/scanned candidate 페이지 → asset 인덱싱, 빈 페이지 0 chunk, P7-1
  emit 한 `Provenance::Warning` 그대로 통과. 향후 OCR fallback 까지는
  검색 불가 (out of scope).
- determinism stress: extract → chunk 사이에 `now()` 추가 호출 금지
  (P6-4 와 동일 invariant).
- 11 통합 테스트 + smoke 업데이트 (별도 implementation PR).

`tasks/INDEX.md` P7 components 2 → 3 반영.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 09:07:03 +00:00