fix(ci): restore green main after merge skew and new advisory

Main CI failed on c09730e87 for two unrelated reasons, and the local
pre-push Rust lane also tripped on a load-sensitive test.

Mobile: #8223 was tested against a base from before #8206, which
removed buzzDevPushAppProfile along with app-profile selection. Its new
notification toggle test still put `appProfile` in the native grant map,
so the merged tree did not compile. The field no longer exists, so drop
it.

Security: cargo-deny now reports RUSTSEC-2024-0449 (unsafe-libyaml is
unmaintained), pulled in through serde_yaml. This is an unmaintained
notice, not a vulnerability. Removing the crate means migrating YAML
parsing across several crates, so ignore it with a reason, like the
existing unmaintained entries.

buzz-acp: the spawn_script test helper returned before bash was running,
and timing tests start their idle clock right away. Under full-suite
load, process startup alone took longer than the idle windows, so
keepalive_resets_idle_past_deadline, idle_resets_on_stdout_activity and
the steer capture tests timed out before the script wrote anything. The
helper now has the script print a ready line and consumes it before
returning, so each clock starts once the child is executing. The two
keepalive loops use brace expansion instead of $(seq), dropping an exec
from their first gap, and the keepalive test gets a 250ms idle deadline
(750ms floor) for more headroom; without the reset it still fails.

Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
This commit is contained in:
Wes
2026-10-12 10:16:19 -07:00
parent c09730e874
commit 5dbaa4d32d
3 changed files with 35 additions and 11 deletions

View File

@@ -3198,10 +3198,30 @@ mod tests {
); );
} }
/// Spawn `script` under bash and return once it is running.
///
/// Process startup can take hundreds of milliseconds on a loaded host,
/// which would otherwise eat into the idle and timing windows the tests
/// measure. The script prints a ready line first, consumed here, so each
/// test's clock starts only after the child is actually executing.
async fn spawn_script(script: &str) -> AcpClient { async fn spawn_script(script: &str) -> AcpClient {
AcpClient::spawn("bash", &["-c".into(), script.into()], &[], false) const READY: &str = "buzz-acp-test-script-ready";
let mut client = AcpClient::spawn(
"bash",
&["-c".into(), format!("echo {READY}; {script}")],
&[],
false,
)
.await
.expect("failed to spawn test script");
let ready = tokio::time::timeout(std::time::Duration::from_secs(10), client.reader.next())
.await .await
.expect("failed to spawn test script") .expect("test script should start within 10s");
assert!(
matches!(&ready, Some(Ok(line)) if line == READY),
"expected ready line, got {ready:?}"
);
client
} }
#[cfg(unix)] #[cfg(unix)]
@@ -3367,7 +3387,7 @@ mod tests {
// Send valid JSON (session/update notifications) to reset the idle timer. // Send valid JSON (session/update notifications) to reset the idle timer.
// Non-JSON lines no longer reset idle — only valid JSON notifications do. // Non-JSON lines no longer reset idle — only valid JSON notifications do.
let mut client = spawn_script( let mut client = spawn_script(
r#"for i in $(seq 1 10); do echo '{"jsonrpc":"2.0","method":"session/update","params":{"update":{"sessionUpdate":"agent_thought_chunk","content":{"text":"thinking"}}}}'; sleep 0.05; done; sleep 10"#, r#"for i in {1..10}; do echo '{"jsonrpc":"2.0","method":"session/update","params":{"update":{"sessionUpdate":"agent_thought_chunk","content":{"text":"thinking"}}}}'; sleep 0.05; done; sleep 10"#,
) )
.await; .await;
let max_dur = std::time::Duration::from_secs(10); let max_dur = std::time::Duration::from_secs(10);
@@ -3561,10 +3581,10 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn keepalive_resets_idle_past_deadline() { async fn keepalive_resets_idle_past_deadline() {
// Keepalive session/update lines every 50ms against a 100ms idle deadline. // Keepalive session/update lines every 50ms against a 250ms idle deadline.
// The turn should survive well past the 100ms deadline (proves the fix). // The turn should survive well past the idle deadline (proves the fix).
let mut client = spawn_script( let mut client = spawn_script(
r#"for i in $(seq 1 20); do echo '{"jsonrpc":"2.0","method":"session/update","params":{"update":{"sessionUpdate":"keepalive"}}}'; sleep 0.05; done; sleep 10"#, r#"for i in {1..20}; do echo '{"jsonrpc":"2.0","method":"session/update","params":{"update":{"sessionUpdate":"keepalive"}}}'; sleep 0.05; done; sleep 10"#,
) )
.await; .await;
let max_dur = std::time::Duration::from_secs(10); let max_dur = std::time::Duration::from_secs(10);
@@ -3574,16 +3594,16 @@ mod tests {
.read_until_response_with_idle_timeout( .read_until_response_with_idle_timeout(
"test", "test",
999, 999,
std::time::Duration::from_millis(100), std::time::Duration::from_millis(250),
hard_deadline, hard_deadline,
max_dur, max_dur,
) )
.await; .await;
let elapsed = start.elapsed(); let elapsed = start.elapsed();
// 20 keepalives × 50ms = ~1000ms of activity, then idle fires after 100ms more. // 20 keepalives × 50ms = ~1000ms of activity, then idle fires after 250ms more.
// Must survive well past the 100ms deadline. // Must survive well past the 250ms deadline.
assert!( assert!(
elapsed >= std::time::Duration::from_millis(500), elapsed >= std::time::Duration::from_millis(750),
"keepalive should reset idle past the deadline; elapsed only {elapsed:?}" "keepalive should reset idle past the deadline; elapsed only {elapsed:?}"
); );
assert!(elapsed < std::time::Duration::from_secs(5)); assert!(elapsed < std::time::Duration::from_secs(5));

View File

@@ -15,6 +15,11 @@ ignore = [
# remove these when upstream catches up. # remove these when upstream catches up.
{ id = "RUSTSEC-2026-0194", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" }, { id = "RUSTSEC-2026-0194", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" },
{ id = "RUSTSEC-2026-0195", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" }, { id = "RUSTSEC-2026-0195", reason = "transitive via rust-s3 and mesh-llm→plist; trusted-input XML only; no upstream fix available yet" },
# unsafe-libyaml 0.2.11 — unmaintained (repository archived 2024-03-25), not a
# vulnerability report. Transitive dep: serde_yaml → unsafe-libyaml. The serde_yaml_ng
# fork uses the same crate, so removing it means migrating YAML parsing in
# buzz-agent, buzz-persona, buzz-relay, buzz-workflow and desktop.
{ id = "RUSTSEC-2024-0449", reason = "transitive via serde_yaml; unmaintained, not vulnerable; removal needs a YAML parser migration" },
] ]
[licenses] [licenses]

View File

@@ -78,7 +78,6 @@ void main() {
'installationId': 'b' * 32, 'installationId': 'b' * 32,
'endpointGrant': 'test-endpoint-grant', 'endpointGrant': 'test-endpoint-grant',
'endpointHash': 'c' * 64, 'endpointHash': 'c' * 64,
'appProfile': buzzDevPushAppProfile,
'endpointEpoch': 1, 'endpointEpoch': 1,
'generation': 1, 'generation': 1,
'expiresAt': DateTime.now().millisecondsSinceEpoch ~/ 1000 + 3600, 'expiresAt': DateTime.now().millisecondsSinceEpoch ~/ 1000 + 3600,