chore(runtime): promote v0.8.4 from canary to the default image
Every mission on gw-04 was already running v0.8.4 — pinned by CLAWMATES_RUNTIME_IMAGE in .env. The canary is retired: the default tag `clawmates-runtime:sync` now IS that image, the override is commented out, and the built-in default is the single source of truth again. Promoting it exposed why the pin was load-bearing in the first place. The default tag resolved to zeroclaw 0.8.3 — two releases behind what was actually running — and the REGISTRY copy of the same tag was a different image again, 849MB against 2.31GB, without the Rust toolchain. A host that pulled `sync` rather than retagging it would have lost the on-green test gate with every probe still reporting success. A moving tag pointing somewhere old resolves perfectly, starts perfectly, and runs old code. Nothing anywhere said which image a mission got, so two things now do: - mission_runtime logs the image it resolved and whether that came from the env override or the built-in default, once at startup. - runtime_preflight probes `zeroclaw --version` alongside the other tools and prints every tool's VERSION, not just that it is present. A presence check passes happily on an image two releases behind, which is exactly what happened here and was found by running the binary by hand. Rollback is a retag: `clawmates-runtime:pre-v084-default` on gw-04 holds the previous default, and .env.pre-v084-default holds the previous pin. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d810fc0a86
commit
cdc45bd082
@@ -36,9 +36,20 @@ use futures::StreamExt;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// Docker image the per-mission runtime uses. Matches the current
|
/// Docker image the per-mission runtime uses, overridable per-deploy via
|
||||||
/// singleton `clawmates-runtime` image; can be overridden per-deploy
|
/// `CLAWMATES_RUNTIME_IMAGE` (for a canary; the default is the shipped one).
|
||||||
/// via `CLAWMATES_RUNTIME_IMAGE`.
|
///
|
||||||
|
/// A MOVING tag, and that is the hazard: it names whatever the host last
|
||||||
|
/// tagged. gw-04's `sync` was two zeroclaw releases behind the canary every
|
||||||
|
/// mission was actually running, and there was no way to see it — a moving tag
|
||||||
|
/// that points somewhere old resolves perfectly, starts perfectly, and runs old
|
||||||
|
/// code. The registry copy of the same tag was a DIFFERENT image again, without
|
||||||
|
/// the Rust toolchain, so a host that pulled rather than retagged would have
|
||||||
|
/// silently lost the on-green test gate.
|
||||||
|
///
|
||||||
|
/// Two things make that visible now: this module logs the image it resolved and
|
||||||
|
/// `runtime_preflight` reports the versions inside the container, not just that
|
||||||
|
/// the binaries exist.
|
||||||
const DEFAULT_IMAGE: &str = "clawmates-runtime:sync";
|
const DEFAULT_IMAGE: &str = "clawmates-runtime:sync";
|
||||||
|
|
||||||
/// Well-known ZeroClaw gateway port.
|
/// Well-known ZeroClaw gateway port.
|
||||||
@@ -568,8 +579,13 @@ impl MissionRuntimeProvisioner {
|
|||||||
} else {
|
} else {
|
||||||
Docker::connect_with_local_defaults().ok()?
|
Docker::connect_with_local_defaults().ok()?
|
||||||
};
|
};
|
||||||
let image =
|
let (image, source) = match std::env::var("CLAWMATES_RUNTIME_IMAGE") {
|
||||||
std::env::var("CLAWMATES_RUNTIME_IMAGE").unwrap_or_else(|_| DEFAULT_IMAGE.to_string());
|
Ok(v) if !v.trim().is_empty() => (v, "CLAWMATES_RUNTIME_IMAGE"),
|
||||||
|
_ => (DEFAULT_IMAGE.to_string(), "the built-in default"),
|
||||||
|
};
|
||||||
|
// Said once at startup, because every mission on this host runs in it
|
||||||
|
// and nothing else names it out loud.
|
||||||
|
eprintln!("mission_runtime: per-mission runtime image = {image} (from {source})");
|
||||||
Some(MissionRuntimeProvisioner { docker, image })
|
Some(MissionRuntimeProvisioner { docker, image })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ struct Dependency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DEPENDENCIES: &[Dependency] = &[
|
const DEPENDENCIES: &[Dependency] = &[
|
||||||
|
Dependency {
|
||||||
|
argv: &["zeroclaw", "--version"],
|
||||||
|
needed_for: "driving every container-tier turn; the version is also how \
|
||||||
|
a runtime image that silently rolled back is noticed",
|
||||||
|
},
|
||||||
Dependency {
|
Dependency {
|
||||||
argv: &["cargo", "--version"],
|
argv: &["cargo", "--version"],
|
||||||
needed_for: "the on_green_tests gate for Rust repos; without it every \
|
needed_for: "the on_green_tests gate for Rust repos; without it every \
|
||||||
@@ -193,10 +198,20 @@ pub fn report_at_boot() {
|
|||||||
let missing: Vec<&ToolStatus> = tools.iter().filter(|t| !t.present).collect();
|
let missing: Vec<&ToolStatus> = tools.iter().filter(|t| !t.present).collect();
|
||||||
if missing.is_empty() {
|
if missing.is_empty() {
|
||||||
let names: Vec<&str> = tools.iter().map(|t| t.program.as_str()).collect();
|
let names: Vec<&str> = tools.iter().map(|t| t.program.as_str()).collect();
|
||||||
|
// The VERSIONS, not just the names. A tag that quietly
|
||||||
|
// points at an older build passes a presence check
|
||||||
|
// perfectly: gw-04's default tag was two zeroclaw releases
|
||||||
|
// behind while every probe said "present", and the only way
|
||||||
|
// anyone found out was running the binary by hand.
|
||||||
|
let detail: Vec<String> = tools
|
||||||
|
.iter()
|
||||||
|
.map(|t| format!("{}={}", t.program, t.detail))
|
||||||
|
.collect();
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"runtime_preflight: `{container}` has all {} expected tools ({})",
|
"runtime_preflight: `{container}` has all {} expected tools ({}) — {}",
|
||||||
tools.len(),
|
tools.len(),
|
||||||
names.join(", ")
|
names.join(", "),
|
||||||
|
detail.join("; ")
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user