3 Commits
Author SHA1 Message Date
zeekay 0b132a8881 Name the thing instead of calling it a door
One word stood for several: the MCP server, one address that accepts requests,
the single way in, and the boundary a credential is validated at. Each site now
names its own sense — endpoint, entry point, the MCP server, the edge, address,
relay — or, where none of those fit, takes the word the surrounding code already
uses.

Prose and comments only, 1:1 line for line. Identifiers keep their names: CSS
classes, DOM ids, struct fields, enum variants, config values and the doc
comments that must open with the identifier they document.
2026-08-23 13:49:39 -07:00
zeekayandHanzo Dev 0df9fb2432 spec sync asks the repository that holds the document
hanzoai/cloud is the re-rooted OSS core: a twelve-commit line sharing no
ancestor with the product and carrying no openapi.yaml at all. It is PUBLIC, so
the workflow's checkout succeeded and publish.py failed one step later with "no
remote of .cloud carries openapi.yaml on `main`" — which the step then threw
away, printing instead its own fixed sentence, that hanzo.yaml had been
hand-edited. It had not: re-derived at its own pinned ref, the committed
artifact is byte-for-byte what that ref projects to. Every push and every hourly
run has reported a drift that never existed.

So the address is the value it always should have been. SPEC_REPO names
hanzo-inc/cloud — where every ref this repo and every SDK has pinned resolves,
and hashes to the digest its lock already carries — and .spec-lock, info.x-spec
and CAPABILITIES.md's header follow it rather than restating it. The receipt was
repointed on its own once and the artifact, the receipt and the test then named
three different repositories; test_the_artifact_names_the_release_it_came_from
now asserts the three AGREE instead of repeating the string a fourth time.

Both canned ::error:: wrappers are gone. A step that replaces a precise
diagnosis with a fixed sentence is worse than a step with no message: publish.py
already says what is wrong and which command fixes it, and `set -e` fails the
step.

hanzo-inc/cloud is private, and the per-job token Hanzo Git mints reads the
run's OWN repository and anything public — nothing else. Measured on
hanzoai/python-sdk run 91449, which named the right repo and still took
`curl: (22) ... 404`. The checkout therefore takes HANZO_GIT_TOKEN, the one name
in this fleet for a credential that reads git.hanzo.ai, and the job says which
credential is missing before actions/checkout can report a private repo as
"repository not found". generate.py's own fetch takes the same name, so one read
needs one credential.

Cloud declares its own `bearer` scheme now, and the projection's addition
stopped adding and started corrupting: setdefault no longer fired while the
document-level `security` it wrote still did, so the published document required
`bearerAuth` and defined nothing by that name. Generated typescript, measured:
2498 bearer call sites from cloud's document, 0 from the projection, leaving 191
files importing a credential helper none of them called. The rule is gone —
cloud says both halves — and test_a_client_can_authenticate refuses a document
that requires a scheme it does not define, whoever supplies it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-19 23:33:15 -07:00
zeekayandHanzo Dev 353b50d2b6 tools: a tool name is the id the door answers to, not codegen's rename
`publish.py` rule 6 suffixes an operationId when two collide under the identity
a GENERATOR uses — punctuation stripped, case folded — so that Go does not
declare one request type twice. `deleteSession` (POST /v1/iam/sessions/delete)
and `DeleteSession` (DELETE /v1/o11y/sessions) are that collision, and the
second is published as `DeleteSession_2`.

An operationId is not only a codegen identifier. It is the WIRE NAME of an MCP
tool, and `POST /v1/mcp` answers to no `DeleteSession_2` — measured against the
live door, which lists `DeleteSession` and `deleteSession` and nothing else. The
suffix shipped verbatim in the tool catalogue of three client distributions:
npm @hanzo/mcp, PyPI hanzo-mcp, crates.io hanzo-mcp, all three byte-identical at
d6d49a17, 2279 tools. A catalogue that names a tool nothing can be told to run is
the one defect the catalogue exists to remove.

So rule 6 records what it renamed. `x-id` is the id hanzoai/cloud emitted,
written only where the projection had to change it — one operation in 2279, so
its meaning is its presence — and `tools.py`'s `wire()` is the ONE place a tool
takes its name. Nothing else moves: same rules, same drops, same count.

MEASURED, catalogue against the live door's 1323 tools:
  before  1299/1323
  after   1300/1323   and the remaining 23 are exactly the compat-tagged IAM
                      operations rule 2 drops on purpose, each of which has a
                      canonical twin in the published document
                      (add-user -> POST /v1/iam/users, and so on for all 23).

test_tools.py pins it from the document alone, offline: where `x-id` is present
the catalogue must carry it and must NOT carry the codegen name. hanzo.yml runs
the file, because a gate that is not in the `test:` block does not run.

openapi-generator 7.14.0 is indifferent to the new key — python, typescript,
java and kotlin each emit a full client tree from the document that carries it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 18:31:39 -07:00