feat(tui): TUI background ingest worker + status bar (p9-fb-03) #55
Reference in New Issue
Block a user
Delete Branch "feat/p9-fb-03-tui-bg"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
kebab tuiLibrary 의r키가kebab_app::ingest_with_config_progress를 spawned thread 에서 호출 → run loop 가 매 frame 마다 progress channel drain → 화면 하단 status bar 갱신. blocking 하지 않음. spec PR #51 의 §2.4a / §10 + p9-fb-01 의 facade 위에 build. p9-fb-03 spec.변경
신규
crates/kebab-tui/src/app.rs::IngestStateApp.ingest_state: Option<IngestState>slot 추가. p9-fb-04 의 cancel wiring 을 위해 channel 만 allocate, 본 PR 에서 send X.신규
crates/kebab-tui/src/ingest_progress.rsstart_ingest(app)— worker thread spawn + channel allocation.drain_progress(app)— main loop tick 마다 try_recv drain.apply_event(state, event)— kind 별 counter 누적 + terminal mark.status_line(state)— 사람-친화 텍스트 (scanning / 진행 / ✓ 완료 / ✗ abort 4 모드).ready_to_clear(state)—TERMINAL_LINE_HOLD_SECS(3 초) 후 true.Run loop wiring
drain_progress+ready_to_clear→ terminal 후 3 초 경과 시 slot drop + worker join + Library refresh 큐.ingest_stateSome 일 때 footer 위에 status bar 1 줄 추가. 평시 영향 0.r=ingest추가.Status line 형식
미적용 (별 task)
Esc/Ctrl-Ccancel signal → p9-fb-04 (IngestState.cancel_txslot 만 본 PR 에서 정의)Test plan
cargo test -p kebab-tui --lib ingest_progress— 10 PASS (apply_event 5 / status_line 4 / ready_to_clear 2 — 합 11 였으나 status_line 의 in_progress 가 앞에서 setup 한 상태 재사용해 합 10).cargo test -p kebab-tui— 25+ PASS, 기존 15 회귀 0.cargo clippy -p kebab-tui --all-targets -- -D warningsclean.후속
tasks/p9/p9-fb-03-tui-ingest-background.mdstatusin_progress→completed한 줄 commit.회차 1 — 설계 결함 1건 + 가독성 nit 1건 + 칭찬 3건.
핵심 actionable:
cancel_txslot 이 dead channel.start_ingest안에서_cancel_rx가 drop 되므로cancel_tx.send(())가 항상Err. p9-fb-04 가 cancel 을 wire 하려면 어차피IngestStatereshape 필요 → 현 slot 의 미래 호환성 기여 0 + CLAUDE.md 의 "backward-compat shim 금지" 룰 위반 가까움. 권장: 본 PR 에서cancel_txfield 제거, p9-fb-04 PR 에 함께 추가. 또는IngestState.cancel_rx: Option<Receiver<()>>도 함께 보유하는 형태로 변경.render_ingest_status의 aborted-시 Bold 만 — p9-fb-14 (color theme) 후 갈음 가능.총평: spec PR #51 / app facade (#52) / CLI (#53) 위에 4 번째 layer 정확히 추가.
apply_event의 facade authoritative counts replace 패턴이 drift 방지. 위 #1 의 cancel_tx 결정만 재고 후 머지.(칭찬)
IngestState가 spec 본문의 field set 정확히 mirror —rx/counts/current_path/started_at추가로current_idx(status bar 의idx/total표시용) +terminal_at/aborted(final 라인 hold + 시각 구분) +thread(clear 시 join). 모든 fieldpub— p9-fb-04 가 cancel wire 시 외부 mutate 가능. parallel-safety contract 의 "sub-state slot" 패턴 정확.@@ -0,0 +156,4 @@state.counts.scanned.saturating_sub(state.counts.errors),state.counts.scanned,secs,state.counts.new,(설계 결함 / cancel slot 사용 불가)
_cancel_rx를start_ingest안에서 즉시 drop 하면IngestState.cancel_tx.send(())가 항상Err(SendError)— channel 의 receiver 가 사라졌기 때문. p9-fb-04 가 cancel 을 wire 하려면 (a) receiver 도 worker thread 로 move 하거나 (b) IngestState 가 receiver 를 함께 보유해야 함. 현 상태로는 cancel_tx 가 슬롯이 아니라 dead channel — p9-fb-04 가IngestState를 reshape 해야 동작.Why: spec 의 의도는 "slot 만 정의, p9-fb-04 가 send 만 추가". 그러나 receiver 가 같은 함수에서 dropped 면 send 결과가 noop 이라 cancel 신호 자체가 worker 에 도달 못 함. p9-fb-04 가 어차피 IngestState 손대야 한다면 본 PR 의 cancel_tx slot 은 미래 호환성 기여 0 — 차라리 p9-fb-04 까지 미루는 게 정직.
How to apply (둘 중):
IngestState에pub cancel_rx: Option<Receiver<()>>추가,start_ingest가 둘 다 보유. p9-fb-04 가take()해서 worker thread 로 move.cancel_txfield 를 아예 제거하고 p9-fb-04 가 추가하는 것으로 미룸. 어차피 wire 가 함수 시그니처 (ingest_with_config_cancellable) 추가도 동반하므로 IngestState reshape 도 같이 처리.권장: 옵션 2 — slot 만 두는 것은 backward-compat shim 패턴 (CLAUDE.md 금지) 에 가깝고, 실제로 사용도 못 함. p9-fb-04 의 PR 에 함께 추가.
@@ -0,0 +193,4 @@use kebab_app::AggregateCounts;use kebab_core::IngestItemKind;use std::sync::mpsc;(칭찬)
apply_event의Completed { counts }/Aborted { counts }분기에서 facade 의 authoritative counts 로 교체 (state.counts = counts). running totals 와 final report 사이 작은 drift (예: AssetFinished 가 chunks 보고 vs facade 의chunks_indexed = embed_active 시에만 누적) 가 자동 정정.kebab-app::ingest_with_config_progress의 보고와 status bar 가 항상 일치 보장.@@ -32,0 +38,4 @@.ingest_state.as_ref().map(crate::ingest_progress::ready_to_clear).unwrap_or(false);(칭찬)
clear_now분기가take()후thread.take()+join()+ Libraryneeds_refresh큐 — terminal event 후 3 초 hold 동안 사용자가 final 라인 읽고, slot drop 시 worker 정리 + 다음 idle tick 에서 Library 가 재로드 → 새 doc 자동 surface. 사용자 명시 action 없이 신선한 view 보장.(가독성 nit)
render_ingest_status가Span::styled에Modifier::BOLD만 적용 (aborted 시) — color theme (p9-fb-14) 도입 전이라 이번 PR 의 visual 차이는 미미. 다만 ✓ / ✗ 첫 글자만으로도 시각 구분 가능하니 OK. 후속 (p9-fb-14) 에서Theme::warning/Theme::error로 갈음 가능 — coupling 0.회차 2 — 회차 1 의 설계 결함 (cancel_tx dead channel) 정확히 반영. cancel_tx field + channel allocation 제거 + doc 갱신 (p9-fb-04 가 reshape). 추가 actionable 0. APPROVE.