mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
fix(ci): restore green main after merge skew and new advisory (#8277)
Main CI has been red sincec09730e87(#8223). The Mobile and Security checks fail on main, and Docker image fails because it requires a green CI run for the same commit. Locally, the pre-push Rust lane also failed on load-sensitive `buzz-acp` timing tests. This PR fixes all three so main passes locally and on GitHub. - **Mobile:** #8223 was tested against a base from before #8206, which removed `buzzDevPushAppProfile` and app-profile selection. Its new `notification_toggle_test.dart` still put `appProfile` in the native grant map, so the merged tree doesn't compile. This removes the stale field. - **Security:** cargo-deny now reports [RUSTSEC-2024-0449](https://rustsec.org/advisories/RUSTSEC-2024-0449) (`unsafe-libyaml` is unmaintained), which comes in through `serde_yaml`. It's an unmaintained notice, not a vulnerability. The `serde_yaml_ng` fork depends on the same crate, so removing it means migrating YAML parsing in five crates. Like the existing unmaintained entries, it goes in `deny.toml` with a reason. - **buzz-acp test flakes:** the `spawn_script` test helper returned before bash was running, and the timing tests started their idle clock right away. Under full-suite load, process startup took longer than the idle windows. `keepalive_resets_idle_past_deadline`, `idle_resets_on_stdout_activity` and `acp_steer_request_omits_expected_run_id_and_carries_session_and_prompt` failed this way across three pre-push runs. The helper now has the script print a ready line and consumes it before returning. The keepalive loops also use brace expansion instead of `$(seq)`, which avoids an extra process launch before the first line. The keepalive test now uses a 250ms idle window with a 750ms floor. Both tests still fail if keepalives don't reset the idle clock. ### Validation - `cargo-deny check advisories` passes with this `deny.toml` and fails on main's. - The `notification_toggle_test.dart` tests and `flutter analyze` pass. - `cargo test -p buzz-acp --lib` passes all 1055 tests. - The `acp::tests` suite passed 20 of 20 runs with 4 concurrent copies over 5 rounds. Under the same load, the version without the brace-expansion change failed every run. - All pre-push hooks pass on5dbaa4d32: rust-tests, mobile-checks, desktop-tauri-checks, file-size-check, branch-skew. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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");
|
||||
let ready = tokio::time::timeout(std::time::Duration::from_secs(10), client.reader.next())
|
||||
.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)]
|
||||
@@ -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));
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user