feat(ci): protocol version handshake client + IPC benchmark (#556, #342)

#556: HANDSHAKING state in sim_bridge.gd — decodes first framed message
as HandshakeMessage, validates vs Protocol.PROTOCOL_VERSION, 5s timeout,
handshake_complete/handshake_failed signals.

#342: IPC benchmark now reads and validates HandshakeMessage before
starting the timing loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 12:57:59 +01:00
co-authored by Claude Opus 4.6
parent 1bd528e521
commit b6c4ecb302
2 changed files with 83 additions and 16 deletions
+18 -14
View File
@@ -10,9 +10,6 @@
//!
//! Output: IPC_BENCH_RESULT:{json} on a single line for tooling to parse.
//!
//! BLOCKED (#342): Handshake step is stubbed pending #555 (server) + #556 (client).
//! The test currently skips the HandshakeMessage exchange and starts timing
//! immediately after TCP connection is established.
//!
//! Spec references: D-020 (subprocess IPC, 5ms budget), D-030 (Layer 3)
@@ -97,13 +94,20 @@ fn ipc_round_trip_latency() {
let mut reader = BufReader::new(stream.try_clone().expect("clone stream"));
let mut writer = BufWriter::new(stream);
// TODO (#342, #555/#556): Wait for HandshakeMessage here before starting timing.
// When the server sends HandshakeMessage { protocol_version: 14 } as the first
// framed message, read and validate it. If version != PROTOCOL_VERSION, abort.
// The timing loop below starts after a successful handshake.
//
// For now, connect and proceed directly — the timing loop handles whatever
// the server sends as its first message.
// 4. Handshake: read and validate HandshakeMessage before timing (#555/#556).
// Server sends HandshakeMessage { protocol_version } as the very first framed message.
let handshake_bytes = read_framed(&mut reader)
.expect("read handshake")
.expect("server closed before sending HandshakeMessage");
let handshake: HandshakeMessage =
rmp_serde::from_slice(&handshake_bytes).expect("deserialize HandshakeMessage");
assert_eq!(
handshake.protocol_version,
PROTOCOL_VERSION,
"handshake version mismatch: server={}, client={}",
handshake.protocol_version,
PROTOCOL_VERSION
);
let make_input = |tick: u64| PlayerInput {
tick,
@@ -112,7 +116,7 @@ fn ipc_round_trip_latency() {
let mut round_trip_ms: Vec<f64> = Vec::with_capacity(WARMUP_ROUNDS + MEASURE_ROUNDS);
// 4. Warmup rounds (not timed)
// 5. Warmup rounds (not timed)
for tick in 0..WARMUP_ROUNDS as u64 {
let payload =
rmp_serde::to_vec_named(&vec![make_input(tick)]).expect("serialize PlayerInput");
@@ -122,7 +126,7 @@ fn ipc_round_trip_latency() {
.expect("server closed during warmup");
}
// 5. Timed measurement rounds
// 6. Timed measurement rounds
for tick in WARMUP_ROUNDS as u64..(WARMUP_ROUNDS + MEASURE_ROUNDS) as u64 {
let payload =
rmp_serde::to_vec_named(&vec![make_input(tick)]).expect("serialize PlayerInput");
@@ -141,7 +145,7 @@ fn ipc_round_trip_latency() {
round_trip_ms.push(elapsed_ms);
}
// 6. Clean up
// 7. Clean up
drop(reader);
drop(writer);
let exit_deadline = Instant::now() + Duration::from_secs(5);
@@ -163,7 +167,7 @@ fn ipc_round_trip_latency() {
}
}
// 7. Compute percentiles
// 8. Compute percentiles
let mut sorted = round_trip_ms.clone();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());