diff --git a/crates/kebab-tui/src/app.rs b/crates/kebab-tui/src/app.rs index 1791ce8..65ba2c3 100644 --- a/crates/kebab-tui/src/app.rs +++ b/crates/kebab-tui/src/app.rs @@ -177,8 +177,14 @@ impl Default for InspectState { /// the final aggregate counts stay on screen for a few seconds and /// then the slot clears. /// -/// `cancel_tx` is reserved for `p9-fb-04` (Ctrl-C / Esc cancel -/// wiring); this task allocates the channel but never sends on it. +/// `p9-fb-04` adds the cancel surface — at that point this struct +/// gains a real `(cancel_tx, cancel_rx)` pair (the receiver moved +/// into the worker thread alongside the progress sender). We do +/// NOT pre-define a `cancel_tx` slot here because doing so without +/// a matching receiver-bound worker would yield a dead channel +/// (`send` returning `Err(SendError)` forever) — empty slot that +/// pretends to be a future-compat shim is worse than no slot +/// (CLAUDE.md "backward-compat shim 금지"). pub struct IngestState { pub rx: std::sync::mpsc::Receiver, pub counts: kebab_app::AggregateCounts, @@ -195,10 +201,6 @@ pub struct IngestState { /// Worker thread handle. `take()`n at clear time so the join /// happens after the user has had time to read the final line. pub thread: Option>>, - /// Cancel-token slot for `p9-fb-04`. Defined here so that task - /// can wire `Esc` / `Ctrl-C` without restructuring `IngestState`. - /// This task never sends on it. - pub cancel_tx: std::sync::mpsc::Sender<()>, } /// Seconds the final ingest status line stays on screen after a run diff --git a/crates/kebab-tui/src/ingest_progress.rs b/crates/kebab-tui/src/ingest_progress.rs index 1294423..808ba23 100644 --- a/crates/kebab-tui/src/ingest_progress.rs +++ b/crates/kebab-tui/src/ingest_progress.rs @@ -10,9 +10,11 @@ //! seconds (`TERMINAL_LINE_HOLD_SECS`) and then `tick_clear` returns //! true so the run loop can drop the slot. //! -//! `cancel_tx` is allocated here but never sent on — the cancel -//! wiring (`Esc` / `Ctrl-C`) lands in `p9-fb-04`. The slot exists -//! today so this task does not have to reshape `IngestState` later. +//! Cancel surface (Esc / Ctrl-C) lands in `p9-fb-04`; that task +//! adds a `(cancel_tx, cancel_rx)` pair to `IngestState` and +//! threads the receiver through `kebab_app::ingest_with_config_cancellable`. +//! This task does NOT pre-allocate the channel — see the comment on +//! `IngestState` for the rationale. use std::sync::mpsc; use std::thread; @@ -37,11 +39,6 @@ pub fn start_ingest(app: &mut App) -> anyhow::Result<()> { exclude: cfg.workspace.exclude.clone(), }; let (tx, rx) = mpsc::channel::(); - let (cancel_tx, _cancel_rx) = mpsc::channel::<()>(); - // _cancel_rx is intentionally dropped here; p9-fb-04 will wire it - // through to `ingest_with_config_cancellable` (or equivalent). - // Holding both ends today keeps the channel allocated without - // doing anything with it. let cfg_for_thread = cfg; let thread = thread::spawn(move || { kebab_app::ingest_with_config_progress(cfg_for_thread, scope, true, Some(tx)) @@ -55,7 +52,6 @@ pub fn start_ingest(app: &mut App) -> anyhow::Result<()> { terminal_at: None, aborted: false, thread: Some(thread), - cancel_tx, }); Ok(()) } @@ -196,7 +192,6 @@ mod tests { fn fresh_state() -> IngestState { let (_tx, rx) = mpsc::channel::(); - let (cancel_tx, _cancel_rx) = mpsc::channel::<()>(); IngestState { rx, counts: AggregateCounts::default(), @@ -206,7 +201,6 @@ mod tests { terminal_at: None, aborted: false, thread: None, - cancel_tx, } }