Files
cli/tests/spec_drift.rs
zeekay 6d40e148af 1.9.47: the pin could only name a release, and a release is not the document
`.spec-lock` was required to name a hanzoai/cloud release TAG, and a test said so.
That put a second notion of "the document" in a repo whose whole design is that
there is one. The document is emitted from cloud's SOURCE; a tag names a service
release, which lags it. Measured today: cloud's newest tag sat two hours and four
commits behind a main carrying 256 newly typed operations, so the nightly re-pin —
which reads the newest tag — would have moved this repo's pin BACKWARDS onto a
document that describes less than the one already committed. The tag was never the
guarantee it was read as either: a tag is cut before production deploys it, which
the prose beside the rule admitted in the same breath.

So the lock names a COMMIT, the nightly reads main, and the deploy question is
answered by the only thing that can answer it — the live evidence.

That question needed a name, because the gate had been answering it with the wrong
one. A generated coordinate exists because the document declares it, and the chain
digests refuse a hand-edited generated.rs, so "the host denies it and NO document
claims it" could never be true of one. Reporting the three routes cloud's main
declares and production has not deployed yet as PHANTOMs pointed a hard failure at
this repo for someone else's deploy schedule, and buried the real phantom class —
the hand-written literals, which keep the name and keep failing hard. AHEAD counts
them, pins them at 3, and falls on its own when cloud ships.

Re-pinned onto cloud@f1d31fc1e: 2369 -> 2378 operations, 2326 -> 2335 commands,
772 -> 811 typed. The --data census moved 539 -> 524, and reading only that number
tells the wrong story: 93 of the 108 no-schema writes it lost are `ai` alone
(95 -> 2), which cloud did not TYPE but did finally publish — their bodies are
stated and freeform, which is why freeform rose 10 -> 103. Seventeen write commands
genuinely gained typed flags. CONTRADICTED is 0 for the first time; the six stale
/v1/ai nouns left the document with the door that published them. UNFALSIFIABLE
rises 90 -> 92 because production's table stopped naming /v1/agent at all.

Prose may name a scheme now, not only a host: banning the characters `://` banned
the word "otpauth://", which cloud's MFA enrollment hands back for a person to
render as a QR code. What may never appear is an authority after the scheme, which
is the part a client could dial.
2026-08-08 11:56:08 -07:00

136 lines
7.0 KiB
Rust

//! THE DERIVATION GATE — the whole chain, every link, on every `cargo test`.
//!
//! ```text
//! hanzoai/cloud@<tag> openapi.yaml .spec-lock repo, path, ref, sha256
//! │ genspec
//! spec/cloud.json spec/live.json `spec_sha256`
//! │ genproduct
//! src/commands/product/generated.rs genproduct --check
//! ·
//! spec/live.json its own `digest`
//! ```
//!
//! WHAT THIS FILE USED TO BE, and why that was not a gate. It ran `genproduct
//! --check` and asserted that `spec/cloud.json` named a release. Both are
//! comparisons between DERIVED artifacts: `generated.rs` agrees with
//! `spec/cloud.json` for exactly as long as one is derived from the other,
//! whether or not either is still true of any server anywhere. Two stale
//! artifacts agree perfectly. It could not have caught a single defect this repo
//! actually shipped — not the 149 phantom `/v1/cloud/*` commands, not the 46
//! products a stale capture left unreachable, not the four `load-balancers`
//! commands deleted upstream nine minutes after the spec that shipped them.
//!
//! What decides whether a command is real is the SERVER. So `driftgate --refresh`
//! asks it — with a CONTROL for every probe, because a relay door answers
//! identically for a real path and an invented one — and writes down the raw
//! answers as `spec/live.json`. This file then RULES on them, hermetically: no
//! network, no clock, no host. A gate that needs the network in CI gets switched
//! off, and a switched-off gate is worse than none.
use std::process::Command;
fn root() -> &'static str {
env!("CARGO_MANIFEST_DIR")
}
/// The generated product tree is DERIVED, not maintained. This re-runs the
/// derivation over the committed `spec/cloud.json` and fails if the checked-in
/// `generated.rs` is not exactly what comes out — so a hand edit, a half-applied
/// spec refresh, or a generator change landed without regenerating goes red here
/// instead of shipping a command surface no document backs.
///
/// It runs the SAME binary a maintainer runs (`--check` only decides write vs
/// compare), so the gate can never test a different derivation than the one that
/// writes the file.
#[test]
fn generated_is_exactly_what_the_spec_derives() {
let out = Command::new(env!("CARGO_BIN_EXE_genproduct")).arg("--check").output().expect("run genproduct");
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
}
/// …and the spec names a RELEASE, by ref and by digest, and `.spec-lock` agrees.
///
/// This used to assert the spec named the wire (`https://api.hanzo.ai/...`), for
/// a reason that was half right: a capture taken from some local file is a
/// photograph of a moment, and two such photographs shipped as defects — 149
/// phantom `/v1/cloud/*` commands survived one, and 41 platform commands printed
/// their HTTP route where their description belonged because of another.
///
/// But the wire is a photograph too, and a worse one: it names a HOST, and a
/// host cannot say which deploy it was. A capture built from the wire keeps
/// claiming the wire forever while the router moves underneath it, so
/// "provenance names the wire" was satisfied by every stale spec ever committed
/// — it caught neither defect it was written for. What catches them is
/// `genspec --check` re-running the derivation on every push, and that needs a
/// document a ref can NAME.
#[test]
fn the_committed_spec_names_the_release_it_was_generated_from() {
let spec = std::fs::read_to_string(format!("{}/spec/cloud.json", root())).expect("read spec/cloud.json");
let doc: serde_json::Value = serde_json::from_str(&spec).expect("spec/cloud.json is JSON");
let prov = doc.pointer("/info/description").and_then(serde_json::Value::as_str).unwrap_or("");
let lock = std::fs::read_to_string(format!("{}/.spec-lock", root())).expect("read .spec-lock");
let field = |k: &str| {
lock.lines()
.find_map(|l| l.strip_prefix(&format!("{k}=")))
.unwrap_or_else(|| panic!(".spec-lock has no {k}="))
.to_string()
};
let (r#ref, sha) = (field("ref"), field("sha256"));
// AN IMMUTABLE REF, which is a weaker word than "release" and the only one
// that was ever true. This asked for a release TAG, on the grounds that main
// is a document nobody has deployed — but a tag is cut before production
// deploys it too, so the spelling never carried the guarantee it was asked
// for, and it cost the pin a whole notion of its own: cloud's newest tag can
// sit hours behind a main that has typed 256 operations, and taking the tag
// moves the pin BACKWARDS. What actually measures the deploy is the live
// evidence — driftgate's AHEAD, counted and pinned. What this test can decide
// is that the ref names BYTES: a tag or a commit, never a branch and never a
// host, because only those re-derive to the same document twice.
let tag = r#ref.starts_with('v') && r#ref.split('.').count() == 3;
let commit = r#ref.len() == 40 && r#ref.chars().all(|c| c.is_ascii_hexdigit());
assert!(
tag || commit,
".spec-lock names {ref:?}, which is neither a hanzoai/cloud release tag nor a commit. A \
capture must name IMMUTABLE bytes: a branch moves under the digest and a host is not a \
version."
);
assert!(
prov.contains(&format!("hanzoai/cloud@{ref}")) && prov.contains(&format!("sha256:{sha}")),
"spec/cloud.json and .spec-lock disagree about which document this capture is.\n \
spec says: {prov}\n lock says: hanzoai/cloud@{ref} sha256:{sha}\n\
Re-run `make spec`, then commit spec/cloud.json, generated.rs and .spec-lock together."
);
}
/// THE DRIFT GATE ITSELF, run on every `cargo test` because it now needs nothing
/// but a checkout. It asks BOTH questions the two above cannot:
///
/// * is every coordinate a command addresses still SERVED? (evidence from the
/// host, with a control probe, so a `403` wall answering for everything
/// cannot read as "present" and a `404` means what it says)
/// * is every served product REACHABLE, asked of the built binary, unless
/// `src/curation.rs` excuses it with a reason?
///
/// …and it refuses a hand-edited artifact anywhere in the chain: `spec/live.json`
/// carries the digest of the `spec/cloud.json` it is evidence about and a digest
/// of its own payload, so an edit to either stops matching.
///
/// It execs the same binary `make verify` runs, for the same reason
/// `generated_is_exactly_what_the_spec_derives` execs `genproduct`: a gate that
/// re-implements the rule it is gating tests its own copy of it.
#[test]
fn the_live_server_contradicts_this_command_tree_in_neither_direction() {
let out = Command::new(env!("CARGO_BIN_EXE_driftgate"))
.args(["--hanzo", env!("CARGO_BIN_EXE_hanzo")])
.output()
.expect("run driftgate");
assert!(
out.status.success(),
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
}