main
tag: launch-2026-06-01
tag: magicmirror-v0.1.1
tag: magicmirror-v0.3.0
tag: mnw-cli-v0.1.2
tag: mnw-cli-v0.1.3
tag: mnw-cli-v0.1.4
tag: pom-v0.4.1
tag: pom-v0.4.2
tag: pom-v0.4.3
tag: pom-v0.4.4
tag: pom-v0.4.5
tag: wam-v0.3.0
tag: wam-v0.3.1
Files
Commits
Tags
Notes
Issues
bento: wire the agent protocol-version skew check (Run 3 wiring)
HealthResponse.version / PROTOCOL_VERSION were advertised and documented as the
mechanism to "detect a skewed agent at preflight" but no client ever compared
the value — preflight discarded it. preflight() now checks it: version 0 (a
legacy agent predating the field) is tolerated, a mismatched non-zero version is
refused with a clear upgrade message, so skew surfaces before a run instead of
mid-stream. e2e test added. ops-exec 41 tests; clippy -D clean.
Co-Authored-By
Claude Opus 4.8 <noreply@anthropic.com>
2 files changed,
+31 insertions,
-2 deletions
172
172
}
173
173
174
174
async fn preflight(&self) -> Result<()> {
175
-
self.health().await.map(|_| ()).with_context(|| {
175
+
let health = self.health().await.with_context(|| {
176
176
format!(
177
177
"agent /health failed for `{}` — is ops-agent running in the Aqua session on the build host?",
178
178
self.host_label,
179
179
)
180
-
})
180
+
})?;
181
+
// Detect a skewed agent here rather than mid-stream. version 0 = a legacy
182
+
// agent that predates the field (HealthResponse.version defaults to 0);
183
+
// tolerate it, but refuse a future major we don't speak.
184
+
anyhow::ensure!(
185
+
health.version == 0 || health.version == crate::wire::PROTOCOL_VERSION,
186
+
"agent `{}` speaks wire protocol v{}, but this client expects v{} — upgrade the mismatched side",
187
+
self.host_label,
188
+
health.version,
189
+
crate::wire::PROTOCOL_VERSION,
190
+
);
191
+
Ok(())
181
192
}
182
193
183
194
fn capabilities(&self) -> &CapabilitySet {
81
81
assert_eq!(out.stdout, b"signed-ok");
82
82
}
83
83
84
+
#[tokio::test]
85
+
async fn preflight_accepts_a_matching_protocol_version() {
86
+
let base = spawn_agent(
87
+
vec![CallerGrant {
88
+
identity: "fw13".into(),
89
+
actuate: vec!["build".into()],
90
+
observe: vec![],
91
+
}],
92
+
builder_grant(),
93
+
)
94
+
.await;
95
+
let rpc = AgentRpc::new(base, "mbp", CapabilitySet::from_tokens(["build"], Vec::<&str>::new()));
96
+
// The agent advertises PROTOCOL_VERSION; preflight must accept it.
97
+
let health = rpc.health().await.unwrap();
98
+
assert_eq!(health.version, ops_exec::wire::PROTOCOL_VERSION);
99
+
rpc.preflight().await.expect("preflight accepts a same-version agent");
100
+
}
101
+
84
102
#[tokio::test]
85
103
async fn agent_denies_action_outside_its_grant() {
86
104
// The agent host grants build/sign only; the caller asks to deploy. Even