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 # 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 # 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 # 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. # reachability line, not the identity proof; get_account_keys is still that.
operations: [get_models, get_keys] operations: [get_models, get_account_keys]
notes: >- notes: >-
Verified with a nonsense-sibling control: GET /v1/keys answers 403 The keys route answers under the app that owns it — /v1/account/keys — and
{"code":"forbidden","error":"sign in to manage API keys"} with NO key and the bare /v1/keys this file used to name is a 404 today. Re-probed with the
with a bogus one, while GET /v1/keys-zzq9 answers 404 — so the 403 is this nonsense-sibling control the moment it moved: GET /v1/account/keys answers
route refusing, not a wildcard relay. With a real key it returns the 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. caller's own keys. An SDK's hello must be the call that says no.
@@ -118,19 +120,24 @@ flows:
store: store:
summary: KV round-trip — create a store, read it back, delete it. 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: >- notes: >-
The delete belongs in the language's finally/defer, so a failed read still 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. 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 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 its served address, /v1/provisioning/kv.
legacy dialect the binary itself tags compat, and the publication drops a This file named /v1/instances/kv before, and that address is GONE: probed
compat operation by that declaration — so the day this file named them it at api.hanzo.ai it answers 404 and so does its /v1/instances/kv-zzq9
would have named ids the published document does not have, which is control, which is this repo's own definition of refuted. The store plane
exactly what test_flows.py went red on. Probed at api.hanzo.ai: answers under the app that registers it — GET /v1/provisioning/kv answers
GET /v1/instances/kv answers 403 while /v1/instances/kv-zzq9 answers 404, 403 {"code":"forbidden","detail":"a validated principal is required"} while
which is the existence proof this file requires (auth runs before the /v1/provisioning/kv-zzq9 answers 404, the existence proof this file
handler; only a mounted route can refuse). 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: agent:
summary: Create an agent, run it, read the run back. summary: Create an agent, run it, read the run back.
+18 -2
View File
@@ -482,7 +482,23 @@ def main():
return 0 return 0
repo = checkout(a.cloud) 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: if a.current:
tip = git(repo, "rev-parse", branch).strip() tip = git(repo, "rev-parse", branch).strip()
@@ -499,7 +515,7 @@ def main():
f"{tip[:8]} with a different document — run `python3 publish.py`") f"{tip[:8]} with a different document — run `python3 publish.py`")
return 1 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"): if a.check and not have.get("ref"):
sys.exit("publish: --check needs a .spec-lock naming the document this " sys.exit("publish: --check needs a .spec-lock naming the document this "
"artifact is a projection of; run `python3 publish.py` first") "artifact is a projection of; run `python3 publish.py` first")