Files
Kigi-CLI/crates/codegen/kigi-pager-pty-harness/src/scenarios/empty_enter_send_now.rs
T
ZacharyZhang-NY a02b555e66 docs(comments): rewrite comments across all crates to the guidelines
Sweep every first-party crate source (1956 .rs files) to the project comment
guidelines: delete redundant restatements, decorative banners, change
narration, and end-of-line comments; keep and tighten the crucial ones
(invariants, bug rationale, SAFETY blocks, ported-source attribution).

No functional code changed. Every edit is proven comment-only against the
prior tree by a comment-stripping lexer (string/char/raw-string aware) plus a
separate doctest-fence check. Where removing a comment made rustfmt or clippy
want to re-lay-out adjacent code, the minimal triggering comment is restored so
code tokens stay byte-identical.

Gates green: cargo fmt --all --check (0 diffs), cargo check and cargo clippy
--workspace --all-targets (0 warnings).

Adds scripts/check_codegen_comment_guidelines.py — the enforcement gate for
these guidelines (flags banners, end-of-line comments, change narration, and
commented-out code).
2026-07-23 16:55:39 -04:00

122 lines
4.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Empty-composer Enter sends the top mid-turn queued follow-up now.
//!
//! Regression for send-now discoverability: plain Enter with text still
//! *queues*; a second bare Enter on the empty prompt is cancel-and-send — the
//! running turn is cancelled (silently: no "Turn cancelled by user" marker)
//! and the queued row runs as the next turn, arriving on the wire as a
//! standard `<user_query>` prompt with no interjection preamble.
use std::time::Duration;
use anyhow::{Context, Result, bail};
use super::wait_for_welcome;
use crate::{ContentController, PtyHarness, pager_binary};
const DEFAULT_ROWS: u16 = 50;
const DEFAULT_COLS: u16 = 120;
/// The interjection-merge preamble: send-now must never produce it.
const INTERJECTION_WIRE_PREFIX: &str = "The user sent a message while you were working";
fn slow_turn_text(sentinel: &str) -> String {
let mut s = String::from(sentinel);
for i in 0..30 {
s.push_str(&format!(" streaming{i}"));
}
s
}
fn all_user_message_blobs(content: &ContentController) -> Vec<String> {
content
.request_bodies()
.iter()
.flat_map(|b| {
let items = b["messages"].as_array().or_else(|| b["input"].as_array());
items
.into_iter()
.flatten()
.filter(|m| m["role"] == "user")
.map(|m| match m["content"].as_str() {
Some(s) => s.to_owned(),
None => m["content"].to_string(),
})
.collect::<Vec<_>>()
})
.collect()
}
pub async fn assert_empty_enter_force_sends_top_queued() -> Result<()> {
let content = ContentController::start()
.await
.context("start ContentController")?;
// Gate turn 1's terminal event so the queue + empty-Enter provably land
// mid-turn — a paced-chunk window races turn end on slow (remote) workers.
content.hold_agent_completions();
content.set_turns([
slow_turn_text("TURNONE"),
"TURNTWO reply to the promoted follow-up.".to_owned(),
]);
let binary = pager_binary().context("resolve pager binary")?;
let mut harness =
PtyHarness::spawn_with_content(&binary, DEFAULT_ROWS, DEFAULT_COLS, &content, &[])
.context("spawn pager")?;
wait_for_welcome(&mut harness).await?;
harness.inject_keys(b"go\r").context("submit prompt")?;
harness
.wait_for_text("TURNONE", Duration::from_secs(30))
.context("turn 1 streaming")?;
harness
.inject_keys(b"please also check the logs\r")
.context("queue follow-up")?;
harness
.wait_for_text("please also check the logs", Duration::from_secs(10))
.context("queued text visible")?;
harness.inject_keys(b"\r").context("empty Enter send-now")?;
// Release the gate so a completion racing the abort resolves rather than
// hangs.
content.release_agent_completions();
// The " " prefix distinguishes the committed prompt block from the
// prefix-less queue row.
harness
.wait_for_text(
"\u{276F} please also check the logs",
Duration::from_secs(15),
)
.context("promoted prompt scrollback chrome")?;
harness
.wait_for_text("TURNTWO", Duration::from_secs(40))
.context("promoted turn reply")?;
if harness.contains_text("Turn cancelled by user") {
bail!(
"send-now cancel must not render a cancelled marker\n{}",
harness.screen_contents()
);
}
let users = all_user_message_blobs(&content);
let Some(promoted) = users
.iter()
.find(|u| u.contains("please also check the logs"))
else {
bail!("queued follow-up never reached the wire: {users:#?}");
};
if promoted.contains(INTERJECTION_WIRE_PREFIX) {
bail!("send-now must not use the interjection preamble: {promoted}");
}
if !promoted.contains("<user_query>") {
bail!("send-now must arrive as a standard user_query prompt: {promoted}");
}
if harness.contains_text("panicked") {
bail!("pager panicked\n{}", harness.screen_contents());
}
harness.quit().context("clean quit")?;
Ok(())
}