리뷰 두 건에서 나온 지적을 반영한다.
1) sweep 이 끝나도 asset 진행바가 복구되지 않았다 (HIGH)
sweep 은 asset 진행바를 빌려 쓰면서 자기 라벨과 자기(더 작은) 총계를
씌운다. 그런데 `AssetStarted` 는 위치와 메시지만 세팅하고 길이·스타일은
건드리지 않는다. 그래서 sweep 이 한 번 돌면 그 뒤 색인 구간 전체가
`sweep [====] 4213/21` 로 그려졌다 — 라벨도 분모도 틀린다.
더 나쁜 건 스타일 교체가 v0.26.1 의 커스텀 키 `{asset_elapsed}` 를 같이
날린다는 점이다. 느린 asset 에서 `(Ns)` 가 도는 게 "멈춘 게 아님"의 유일한
신호인데 sweep 이 그걸 없앤다. 이 PR 이 sweep 구간에서 없앤 "hang 처럼
보임" 을 asset 구간에 새로 만드는 셈이었다.
바 세팅을 `dress_bar_for_assets` 로 빼고 `ScanCompleted` 와
`SweepCompleted` 양쪽에서 부른다. 스타일을 길이보다 먼저 세팅하는데,
indicatif 가 두 호출 사이에 다시 그릴 수 있어 과도기 프레임이 최소한
올바른 라벨을 달게 하기 위해서다.
TTY 전용이라 비-TTY 실측만 보고 있어서 놓쳤다. pty 로 재현해 고친 뒤
`ingest [====] 16/17 doc9.md` 로 나오는 것을 확인했다.
2) docs/DOGFOOD.md §1.8 이 갱신되지 않았다 (MEDIUM)
`--json` 이벤트 순서 목록에 sweep 3종이 빠져 있었다. verify 항목에
sweep 분모·연속성과 "sweep 뒤 진행바가 asset 분모로 돌아오는가" 를 넣었다
— 1번이 정확히 그 항목이 있었으면 잡혔을 결함이다.
3) ingest_progress.rs 의 ordering invariant 주석이 stale 했다 (MEDIUM)
§2.4a 순서 블록에 sweep 이 없었다. 설계 문서 자체는 frozen baseline 이고
HOTFIXES dated entry 가 있으니 규약상 문제없지만 코드 주석은 living 이다.
4) 잔가지 (LOW)
- purge 실패가 "디스크에 남아 있어 그냥 뒀다" 와 구분되지 않았다. 둘 다
`removed: false` 이고 ndjson 에는 아무것도 안 남아,
`sweep_summary` 의 `checked - purged` 차이로도 못 가른다. 사후 기록이
로그뿐이라는 게 이 PR 의 전제이므로 `purge_failed` 를 추가했다.
- `SweepProgress` 가 purge 할 때만 바 메시지를 세팅하고 비우지 않아,
마지막 purge 경로가 이후 후보를 훑는 내내 남았다. 안 지울 때는 비운다.
- 주석과 스키마가 신규 이벤트를 `v0.33.0` 이라고 적었는데, CLAUDE.md 의
bump 규칙상 이 변경은 patch 다 (additive-only wire + 관측성 개선, 새
명령·플래그·config 없음, 검색·색인 결과 불변 — 선례가 asset_phase).
`v0.32.1` 로 고쳤다.
미반영: `SweepCompleted` 가 TTY 에서 `bar.println` 대신 stderr 에 직접
쓰는 것 — 기존 `AssetTimings`/`PdfOcr*` 이 같은 패턴이라 신규 회귀가 아니고,
바꾸려면 그 셋을 같이 옮겨야 해서 별 건이다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017c9JwQq8ZkGvYjpKXMiDhF
`sweep_deleted_files` 는 walker 가 끝난 직후 asset 루프 전에 도는데, 이
구간이 관측 가능한 신호를 하나도 내지 않았다. 진행바는 walker 총계
(`0/12115`)를 표시한 채 멈춰 보이고 ndjson 로그는 0바이트로 남는다.
`tracing::info!` 은 나가지만 wire 이벤트가 아니라 사용자가 볼 산출물이
없다. 프로세스는 CPU 100% 로 정상 동작 중인데 밖에서는 hang 과 구별할 수
없고, 실제 도그푸딩에서 세 번 연속 Ctrl-C 로 죽였다.
`ingest_progress.v1` 에 세 이벤트를 추가한다 (additive — 기존 소비자는
모르는 kind 를 무시한다).
sweep_started { total }
sweep_progress { idx, total, path, removed }
sweep_completed { checked, purged, ms }
`total` 은 `all_workspace_paths()` 에서 이번 스캔이 덮은 경로를 뺀 값이라
루프 진입 전에 확정 분모가 나온다 (이슈 제안 1). `removed` 는 "정말 없어서
문서를 지웠다" 와 "아직 디스크에 있어 그대로 뒀다" 를 가른다 — 수천 건을
훑고 하나도 안 지우는 sweep 이 있으므로, 지울 때만 움직이는 진행바는 바로
그 경우에 다시 멈춰 보인다.
`purged` 를 두 이벤트에서 다른 타입으로 쓰지 않으려고 sweep_progress 쪽은
`removed`(bool), sweep_completed 쪽은 `purged`(정수) 로 이름을 나눴다.
같은 wire 키가 두 타입을 갖는 건 소비자 입장에서 함정이다.
ndjson 로그에는 `purge { ts, doc_path }` 와 `sweep_summary { ts, checked,
purged, ms }` 를 추가한다 (이슈 제안 2 가 요청한 형태). 로그가 유일한 사후
기록이다 — tracing 은 stderr 로 흘러가고 남지 않는다.
CLI 는 sweep 을 asset 진행바와 별 phase 로 그린다. 후보 12k 를 훑는 일과
asset 12k 를 색인하는 일은 분모가 다른 별개의 작업이라 카운터를 공유하면
두 번째 구간이 처음부터 다시 시작하는 것처럼 보인다. 비-TTY 는 실제로
지운 것만 줄로 찍는다 — 검사한 후보마다 한 줄이면 그대로 둔 경로들이
run 의 진짜 출력을 덮는다.
실측 (문서 30건 색인 → 21건 삭제 → 재색인):
ingest: sweeping 21 deleted-file candidates…
purged doc11.md
… (21줄)
ingest: sweep complete (checked=21 purged=21 in 134ms)
`--json` 은 세 이벤트를 ingest_progress.v1 로 내보내고, ndjson 로그에는
purge 21줄 + sweep_summary 1줄이 남는다.
이슈가 참고로 적은 `reset --orphans-only` 는 그대로 뒀다. reset 에는 진행
채널 자체가 없어 sweep 하나를 위해 배선을 새로 깔아야 하는데, #229 와
#230 이 머지된 지금 이 경로의 문서당 비용이 약 800배 떨어져 "몇 시간
무표시" 상황이 애초에 안 나온다.
곁다리 — clippy 게이트가 붉었다:
`cargo clippy --workspace --all-targets -- -D warnings` 가 main 에서
실패하고 있었다. 툴체인이 올라가면서 새 lint 둘(`question_mark`,
`manual_assert_eq`)이 기존 코드에 걸린 것이고 각각 한 줄이다. 방치하면 안
되는 이유를 이번에 겪었다 — `kebab-parse-code` 가 먼저 실패해 뒤 크레이트가
아예 컴파일되지 않았고, 그 그늘에 PR #235 에서 내가 넣은
`unnested_or_patterns` 위반이 숨어 있었다. 셋 다 여기서 고친다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017c9JwQq8ZkGvYjpKXMiDhF
진행 로그 개선은 검색·색인 결과 불변 + 새 명령/플래그/config 없음 + additive-only
wire(asset_phase)라 CLAUDE.md 신규 규칙(기능/인터페이스 변경=minor, 없으면 patch)상
patch 가 맞음. version·라벨·HOTFIXES 헤더를 0.26.1 로 정정.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
신규 진행로깅 표면(asset_phase / ocr_ms / caption_ms + progress.rs heartbeat·
slowest 주석)이 v0.26.0 으로 잘못 표기돼 있던 것을 v0.27.0(실제 추가 버전)으로
정정. wire schema 의 "추가 버전" 정확성(외부 통합 참조). 로직 변경 없음(주석/doc).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- store_ms 에서 stale-vector orphan purge(LanceDB I/O) 제거 → embed/vector phase
(embed_ms)로 이동. store_ms 가 이제 SQLite put_* 만 의미(진단 정확도; 편집
재색인 시 920ms 오귀속 제거). purge 는 여전히 unconditional + upsert 이전.
- 최종 expansion_progress 프레임을 done != last_done 로 가드 (throttle 배수 시
중복 프레임 + chunks==0 시 0/0 프레임 제거).
- schema/HOTFIXES: store_ms/embed_ms 설명 정정 + dangling IMPL_REPORT 참조 제거.
clippy -D warnings 0, test 312 passed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
asset(문서) 단위뿐이던 ingest 진행 이벤트에 문서 내부 phase 가시성을 추가.
큰 문서가 expansion(별칭 LLM, 청크당 순차)으로 수십 분 걸려도 진행바가
1/N 에 멈춘 듯 보이던 문제 해결.
wire ingest_progress.v1 additive (backward-compat):
- asset_chunked {idx,total,chunks} — 청킹 직후, markdown/image/pdf 전 경로
- expansion_progress {idx,total,done,chunks} — expansion 루프 스로틀
(25청크 또는 1s, 종료 시 done==chunks). 캐시 히트도 done 에 포함
- asset_timings {idx,total,parse_ms,chunk_ms,expansion_ms,embed_ms,store_ms}
— markdown 경로 phase별 wall-clock
설계: timing 은 kebab_core::IngestItem(wire-stable) 변경을 피해 신규
AssetTimings 이벤트로 ingest_one_asset 가 직접 emit (AssetFinished 무변경).
CLI(progress.rs): 진행바 sub-message(→ N chunks / 별칭 확장 done/chunks) +
asset 종료 시 phase timing 한 줄(fmt_ms). TUI reducer no-op arm.
검증: clippy -D warnings exit 0; cargo test -p kebab-app -p kebab-cli
312 passed/0 failed. ordering-invariant 테스트 재작성 + 신규 직렬화 테스트.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- §10 long-running 절 끝 빈 줄 3 → 1 (다른 절 사이 일관)
- wire schema + §2.4a 예제 JSON: kind_result → result (top-level
kind 와의 모호성 제거; ingest_report.v1.items[].kind 와 짝)
- wire schema 의 ts 필드: format: \"date-time\" 추가 (RFC 3339
자동 검증, wrapper 가 다른 format emit 시 즉시 잡힘)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
도그푸딩 후 추가된 long-running 작업 진행 표시 + cancel 정책을 frozen
design 에 명시. p9-fb-01/02/03 (ingest progress callback / CLI display
/ TUI background) 의 spec PR — impl PR 들이 이어진다.
변경:
- docs/wire-schema/v1/ingest_progress.schema.json (신규):
line-delimited streaming event schema. discriminated by `kind`
(scan_started → scan_completed → asset_started → asset_finished* →
embed_batch_* → completed | aborted). 마지막 줄은 기존
ingest_report.v1 그대로 (외부 wrapper backward-compat).
- 2026-04-27-kebab-final-form-design.md §2.4a (신규):
IngestProgressEvent 절. 이벤트 ordering / aborted 의 idempotency /
CLI 의 stderr vs stdout 분리 / TUI · desktop 의 in-memory 소비.
- 2026-04-27-kebab-final-form-design.md §10:
long-running 작업 (ingest, future eval run, RAG streaming, embed
batch) 의 두 invariant — progress 의 단일 source / cooperative
cancel + step boundary. trait (§7.2) 시그니처는 무영향 — facade
hidden parameter 로 추가.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>