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).
122 lines
4.2 KiB
Rust
122 lines
4.2 KiB
Rust
//! 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(())
|
||
}
|