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 {
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")
.expect("failed to spawn test script");
let ready = tokio::time::timeout(std::time::Duration::from_secs(10), client.reader.next())
.await
.expect("test script should start within 10s");
assert!(
matches!(&ready, Some(Ok(line)) if line == READY),
"expected ready line, got {ready:?}"
);
client
}
#[cfg(unix)]
@@ -3367,7 +3387,7 @@ mod tests {
// Send valid JSON (session/update notifications) to reset the idle timer.
// Non-JSON lines no longer reset idle — only valid JSON notifications do.
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;
let max_dur = std::time::Duration::from_secs(10);
@@ -3561,10 +3581,10 @@ mod tests {
#[tokio::test]
async fn keepalive_resets_idle_past_deadline() {
// Keepalive session/update lines every 50ms against a 100ms idle deadline.
// The turn should survive well past the 100ms deadline (proves the fix).
// Keepalive session/update lines every 50ms against a 250ms idle deadline.
// The turn should survive well past the idle deadline (proves the fix).
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;
let max_dur = std::time::Duration::from_secs(10);
@@ -3574,16 +3594,16 @@ mod tests {
.read_until_response_with_idle_timeout(
"test",
999,
std::time::Duration::from_millis(100),
std::time::Duration::from_millis(250),
hard_deadline,
max_dur,
)
.await;
let elapsed = start.elapsed();
// 20 keepalives × 50ms = ~1000ms of activity, then idle fires after 100ms more.
// Must survive well past the 100ms deadline.
// 20 keepalives × 50ms = ~1000ms of activity, then idle fires after 250ms more.
// Must survive well past the 250ms deadline.
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:?}"
);
assert!(elapsed < std::time::Duration::from_secs(5));

View File

@@ -15,6 +15,11 @@ ignore = [
# 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-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]

View File

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