diff --git a/AGENTS.md b/AGENTS.md index 20052ca..c086a6e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -194,6 +194,7 @@ Context: 0.16.x proved that queue tweaks and static calibration cannot guarantee - [x] Calibration may fine-tune sub-frame offsets only; it must not be required to rescue seconds-scale desync. - [x] Bump Lesavka to 0.17.0 because this is a media-contract change, not a patch tune. +- [x] Bump patch follow-ups to 0.17.1 instead of reporting `version+revision` as the release version. - [x] Add planner policy config: max live lag, max skew, startup timeout, target playout delay, and healing cooldown. - [x] Reset 0.17 defaults so shipped audio/video offsets do not intentionally exceed the freshness budget. - [x] Track latest camera/audio input timestamps in the server planner. @@ -222,3 +223,4 @@ Context: 0.16.x proved that queue tweaks and static calibration cannot guarantee - 2026-05-01: Validation green: `cargo test -p lesavka_server --lib --bins`, `cargo test -p lesavka_testing`, `cargo test -p lesavka_client --bins --lib`, and targeted installer/RPC/layout contracts. - 2026-05-01: First installed 0.17.0 mirrored browser probe on client/server commit `3920e0a` failed honestly: planner reported fresh live state (`live_lag_ms=10`, `skew_ms=+20.7`) but browser-observed paired pulses showed audio late by median `+349.1ms`, p95 `429.1ms`, with 6 video freezes/skew drops. Replayed artifact after analyzer hardening now reports `gross_failure` instead of false raw-start `catastrophic_failure`. - 2026-05-01: Patch follow-up models the observed MJPEG/UVC browser egress delta by defaulting video playout offset to `+350ms` and preserving the 1s freshness ceiling. Raw activity-start evidence is now ignored for verdict/calibration when it disagrees with paired pulses that are already failing directly. Existing early-0.17 `audio=0/video=0` factory/env calibration files migrate to the new `video=+350ms` default on load. +- 2026-05-01: Release identity cleanup: bumped the patched build to clean semver `0.17.1`; probe attribution now prints `client_version`/`server_version` separately from `client_revision`/`server_revision` and refuses old `client_full_version` output. diff --git a/Cargo.lock b/Cargo.lock index 4d946b7..b523371 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "lesavka_client" -version = "0.17.0" +version = "0.17.1" dependencies = [ "anyhow", "async-stream", @@ -1686,7 +1686,7 @@ dependencies = [ [[package]] name = "lesavka_common" -version = "0.17.0" +version = "0.17.1" dependencies = [ "anyhow", "base64", @@ -1698,7 +1698,7 @@ dependencies = [ [[package]] name = "lesavka_server" -version = "0.17.0" +version = "0.17.1" dependencies = [ "anyhow", "base64", diff --git a/client/Cargo.toml b/client/Cargo.toml index 3a9f145..5942bbf 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -4,7 +4,7 @@ path = "src/main.rs" [package] name = "lesavka_client" -version = "0.17.0" +version = "0.17.1" edition = "2024" [dependencies] diff --git a/client/src/bin/lesavka-relayctl.rs b/client/src/bin/lesavka-relayctl.rs index bd0978f..650a007 100644 --- a/client/src/bin/lesavka-relayctl.rs +++ b/client/src/bin/lesavka-relayctl.rs @@ -181,10 +181,16 @@ fn print_versions(server_addr: &str, caps: &HandshakeSet) { } else { caps.server_version.as_str() }; + let server_revision = if caps.server_revision.is_empty() { + "unknown" + } else { + caps.server_revision.as_str() + }; println!("client_version={}", lesavka_client::VERSION); - println!("client_full_version={}", lesavka_client::FULL_VERSION); + println!("client_revision={}", lesavka_client::REVISION); println!("server_addr={server_addr}"); println!("server_version={server_version}"); + println!("server_revision={server_revision}"); println!("server_camera_output={}", caps.camera_output); println!("server_camera_codec={}", caps.camera_codec); } diff --git a/client/src/lib.rs b/client/src/lib.rs index 8988ebb..b9d68a1 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -3,8 +3,8 @@ #![forbid(unsafe_code)] pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -pub const BUILD_ID: &str = env!("LESAVKA_GIT_SHA"); -pub const FULL_VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "+", env!("LESAVKA_GIT_SHA")); +pub const REVISION: &str = env!("LESAVKA_GIT_SHA"); +pub const BUILD_ID: &str = REVISION; pub mod app; mod app_support; diff --git a/common/Cargo.toml b/common/Cargo.toml index 12769b0..d6363ae 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lesavka_common" -version = "0.17.0" +version = "0.17.1" edition = "2024" build = "build.rs" diff --git a/common/proto/lesavka.proto b/common/proto/lesavka.proto index 9af1a83..4cd9099 100644 --- a/common/proto/lesavka.proto +++ b/common/proto/lesavka.proto @@ -119,6 +119,7 @@ message HandshakeSet { uint32 eye_height = 9; uint32 eye_fps = 10; string server_version = 11; + string server_revision = 12; } message Empty {} diff --git a/scripts/manual/run_upstream_av_sync.sh b/scripts/manual/run_upstream_av_sync.sh index 27a8a2a..ccee330 100755 --- a/scripts/manual/run_upstream_av_sync.sh +++ b/scripts/manual/run_upstream_av_sync.sh @@ -211,11 +211,26 @@ print_lesavka_versions() { echo "${version_output}" >&2 return 1 fi + if grep -q "^client_full_version=" <<<"${version_output}"; then + echo "Lesavka version query reported a combined version+revision; refusing ambiguous probe attribution" >&2 + echo "${version_output}" >&2 + return 1 + fi + if ! grep -q "^client_revision=" <<<"${version_output}"; then + echo "Lesavka version query did not report client_revision=; refusing to run an unattributed probe" >&2 + echo "${version_output}" >&2 + return 1 + fi if ! grep -q "^server_version=" <<<"${version_output}"; then echo "Lesavka version query did not report server_version=; refusing to run an unattributed probe" >&2 echo "${version_output}" >&2 return 1 fi + if ! grep -q "^server_revision=" <<<"${version_output}"; then + echo "Lesavka version query did not report server_revision=; refusing to run an unattributed probe" >&2 + echo "${version_output}" >&2 + return 1 + fi while IFS= read -r line; do [[ -n "${line}" ]] && echo " ↪ ${line}" done <<<"${version_output}" diff --git a/scripts/manual/run_upstream_mirrored_av_sync.sh b/scripts/manual/run_upstream_mirrored_av_sync.sh index 7fb0a7f..204e8e1 100755 --- a/scripts/manual/run_upstream_mirrored_av_sync.sh +++ b/scripts/manual/run_upstream_mirrored_av_sync.sh @@ -175,11 +175,26 @@ print_lesavka_versions() { echo "${version_output}" >&2 return 1 fi + if grep -q "^client_full_version=" <<<"${version_output}"; then + echo "Lesavka version query reported a combined version+revision; refusing ambiguous probe attribution" >&2 + echo "${version_output}" >&2 + return 1 + fi + if ! grep -q "^client_revision=" <<<"${version_output}"; then + echo "Lesavka version query did not report client_revision=; refusing to run an unattributed probe" >&2 + echo "${version_output}" >&2 + return 1 + fi if ! grep -q "^server_version=" <<<"${version_output}"; then echo "Lesavka version query did not report server_version=; refusing to run an unattributed probe" >&2 echo "${version_output}" >&2 return 1 fi + if ! grep -q "^server_revision=" <<<"${version_output}"; then + echo "Lesavka version query did not report server_revision=; refusing to run an unattributed probe" >&2 + echo "${version_output}" >&2 + return 1 + fi while IFS= read -r line; do [[ -n "${line}" ]] && echo " ↪ ${line}" done <<<"${version_output}" diff --git a/server/Cargo.toml b/server/Cargo.toml index cea602a..ece3f47 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,7 +10,7 @@ bench = false [package] name = "lesavka_server" -version = "0.17.0" +version = "0.17.1" edition = "2024" autobins = false diff --git a/server/src/handshake.rs b/server/src/handshake.rs index d9345ca..ba569c6 100644 --- a/server/src/handshake.rs +++ b/server/src/handshake.rs @@ -34,6 +34,7 @@ impl Handshake for HandshakeSvc { eye_height, eye_fps, server_version: crate::VERSION.to_string(), + server_revision: crate::REVISION.to_string(), })) } } diff --git a/server/src/lib.rs b/server/src/lib.rs index f86f9f6..7f64034 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,8 +1,8 @@ // server/src/lib.rs pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -pub const BUILD_ID: &str = env!("LESAVKA_GIT_SHA"); -pub const FULL_VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "+", env!("LESAVKA_GIT_SHA")); +pub const REVISION: &str = env!("LESAVKA_GIT_SHA"); +pub const BUILD_ID: &str = REVISION; pub mod audio; pub mod calibration; diff --git a/testing/tests/client_manual_sync_script_contract.rs b/testing/tests/client_manual_sync_script_contract.rs index ae25f7f..8b00914 100644 --- a/testing/tests/client_manual_sync_script_contract.rs +++ b/testing/tests/client_manual_sync_script_contract.rs @@ -50,7 +50,10 @@ fn upstream_sync_script_tunnels_auto_server_addr_through_ssh() { "==> Lesavka versions under test", "lesavka-relayctl", "--bin lesavka-relayctl", + "client_revision=", "server_version=", + "server_revision=", + "combined version+revision", ] { assert!( SYNC_SCRIPT.contains(expected), @@ -99,7 +102,10 @@ fn mirrored_sync_script_uses_real_client_capture_path() { "==> Lesavka versions under test", "lesavka-relayctl", "--bin lesavka-relayctl", + "client_revision=", "server_version=", + "server_revision=", + "combined version+revision", ] { assert!( MIRRORED_SYNC_SCRIPT.contains(expected), diff --git a/testing/tests/client_relayctl_binary_contract.rs b/testing/tests/client_relayctl_binary_contract.rs index a379914..17462bc 100644 --- a/testing/tests/client_relayctl_binary_contract.rs +++ b/testing/tests/client_relayctl_binary_contract.rs @@ -60,4 +60,17 @@ mod relayctl_binary { detail: "ok".to_string(), }); } + + #[test] + fn version_output_keeps_release_version_and_revision_separate() { + let source = include_str!(env!("LESAVKA_CLIENT_RELAYCTL_BIN_SRC")); + assert!(source.contains("client_version=")); + assert!(source.contains("client_revision=")); + assert!(source.contains("server_version=")); + assert!(source.contains("server_revision=")); + assert!( + !source.contains("client_full_version="), + "release version output must not append the git revision to semver" + ); + } } diff --git a/testing/tests/handshake_camera_contract.rs b/testing/tests/handshake_camera_contract.rs index 4c04325..74acf5b 100644 --- a/testing/tests/handshake_camera_contract.rs +++ b/testing/tests/handshake_camera_contract.rs @@ -141,6 +141,7 @@ impl Handshake for SparseHandshakeSvc { eye_width: 0, eye_height: 0, eye_fps: 0, + server_revision: String::new(), })) } }