two red gates, neither of them about the artifact

--check asks one question: is hanzo.yaml what its own pinned input projects to?
It answers that at the ref .spec-lock NAMES, so which remote is cloud's main
cannot change the answer — and it was resolving that remote anyway, before
anything read it, and dying there. Measured on main: it exited on `forge/main,
forgeinc/main, inc/main each carry openapi.yaml and have diverged` while the
artifact it was asked about was byte-for-byte correct. The branch is resolved
only where it is used now — `--current`, whose whole question is whether the tip
moved past the pin, and a re-pin with no `--ref` to say where.

The ambiguity itself is real and stays refused. Three remotes carry the document
and none contains the rest; guessing between them is how a projection gets
published from the wrong lineage.

test_flows.py named four operations the document does not have. The keys route
answers under the app that owns it, and the store plane moved again: this file
said /v1/instances/kv, and that address is now 404 with a 404 control, which is
this repo's own definition of refuted. /v1/provisioning/kv answers 403 with a
404 control — served. The notes carried the old probe as if it still held, so
they are re-measured rather than re-worded, and they now say what the bare
/v1/kv actually is: a separate bucket-keyed value plane whose root answers 200
to anyone, which is the one shape a round-trip must not be built on.

Every flow renders into every SDK's examples, so an id here that the document
lacks is a broken example in eight languages.

hanzo.yaml, CAPABILITIES.md and .spec-lock are untouched. The pin stays where it
is: at 22aedc87 the deployment fails to answer for 16 published operations, and
at cloud's main it is 351 — the pin is the newest release api.hanzo.ai answers
for, not the newest release.
This commit is contained in:
zooqueen
2026-08-27 14:39:10 -07:00
parent 0991117a8c
commit 6b8612eb74
2 changed files with 40 additions and 17 deletions
+22 -15
View File
@@ -58,13 +58,15 @@ flows:
# get_openapi.json. So "which routes are open" is a fact an SDK can read
# rather than a probe result recorded in this file, and hello can open with
# a call that SUCCEEDS for a reader who has no credential at all. It is a
# reachability line, not the identity proof; get_keys is still that.
operations: [get_models, get_keys]
# reachability line, not the identity proof; get_account_keys is still that.
operations: [get_models, get_account_keys]
notes: >-
Verified with a nonsense-sibling control: GET /v1/keys answers 403
{"code":"forbidden","error":"sign in to manage API keys"} with NO key and
with a bogus one, while GET /v1/keys-zzq9 answers 404 — so the 403 is this
route refusing, not a wildcard relay. With a real key it returns the
The keys route answers under the app that owns it — /v1/account/keys — and
the bare /v1/keys this file used to name is a 404 today. Re-probed with the
nonsense-sibling control the moment it moved: GET /v1/account/keys answers
403 {"code":"forbidden","detail":"sign in to manage API keys"} with NO key
and with a bogus one, while GET /v1/keys-zzq9 answers 404 — so the 403 is
this route refusing, not a wildcard relay. With a real key it returns the
caller's own keys. An SDK's hello must be the call that says no.
@@ -118,19 +120,24 @@ flows:
store:
summary: KV round-trip — create a store, read it back, delete it.
operations: [post_instances_kv, get_instances_kv_by_name, delete_instances_kv_by_name]
operations: [post_provisioning_kv, get_provisioning_kv_by_name, delete_provisioning_kv_by_name]
notes: >-
The delete belongs in the language's finally/defer, so a failed read still
cleans up instead of leaving the store for the next run to collide with.
This is the provisioning plane — create a KV STORE, read it, drop it — at
its served address, /v1/instances/kv. The bare /v1/kv spellings are the
legacy dialect the binary itself tags compat, and the publication drops a
compat operation by that declaration — so the day this file named them it
would have named ids the published document does not have, which is
exactly what test_flows.py went red on. Probed at api.hanzo.ai:
GET /v1/instances/kv answers 403 while /v1/instances/kv-zzq9 answers 404,
which is the existence proof this file requires (auth runs before the
handler; only a mounted route can refuse).
its served address, /v1/provisioning/kv.
This file named /v1/instances/kv before, and that address is GONE: probed
at api.hanzo.ai it answers 404 and so does its /v1/instances/kv-zzq9
control, which is this repo's own definition of refuted. The store plane
answers under the app that registers it — GET /v1/provisioning/kv answers
403 {"code":"forbidden","detail":"a validated principal is required"} while
/v1/provisioning/kv-zzq9 answers 404, the existence proof this file
requires (auth runs before the handler, so only a mounted route can
refuse).
The bare /v1/kv spellings are a SEPARATE value plane keyed by bucket
(/v1/kv/{bucket}/{key}); the bare /v1/kv itself answers 200 with a package
descriptor to anyone, which is exactly the shape a round-trip must not be
built on.
agent:
summary: Create an agent, run it, read the run back.
+18 -2
View File
@@ -482,7 +482,23 @@ def main():
return 0
repo = checkout(a.cloud)
branch = source(repo)
# The branch is RESOLVED ONLY WHERE IT IS USED, and that is the whole fix for
# a gate that could not run. `source()` fetches every remote and exits when
# several carry the document and none contains the rest — a real ambiguity,
# and refusing to guess at it is right. But it was resolved here,
# unconditionally, and then discarded on the one path that needs nothing from
# it: `--check` re-derives at the ref `.spec-lock` NAMES, so which remote is
# main cannot change its answer. Measured on this repo's own main: `--check`
# exited on `forge/main, forgeinc/main, inc/main each carry openapi.yaml and
# have diverged` while the artifact it was asked about was byte-for-byte
# correct. THE GATE WAS RED FOR A REASON THAT WAS NOT ABOUT THE ARTIFACT.
#
# `--current` genuinely needs it — its whole question is "has the tip moved
# past the pin?" — and re-pinning needs it when no `--ref` says where to pin.
# Those two ask; nothing else does.
pinned = a.ref or (have.get("ref") if a.check else None)
branch = source(repo) if a.current or not pinned else None
if a.current:
tip = git(repo, "rev-parse", branch).strip()
@@ -499,7 +515,7 @@ def main():
f"{tip[:8]} with a different document — run `python3 publish.py`")
return 1
ref = a.ref or (have.get("ref") if a.check else None) or branch
ref = pinned or branch
if a.check and not have.get("ref"):
sys.exit("publish: --check needs a .spec-lock naming the document this "
"artifact is a projection of; run `python3 publish.py` first")