6 Commits
Author SHA1 Message Date
hanzo-dev a361b88677 engine: an operation is named for its product, and its summary is for the caller
CI/CD / gate (push) Canceled after 0s
CI/CD / containment (push) Canceled after 0s
CI/CD / image (push) Canceled after 0s
CI/CD / rollout (push) Canceled after 0s
CI/CD / reach (push) Canceled after 0s
CI/CD / fanout (push) Canceled after 0s
CI/CD / receipt (push) Canceled after 0s
An operation id is the generated SDK METHOD NAME and the CLI COMMAND, and a
summary is what the MCP tool list shows a model choosing between tools. This
plane stated neither, so both were defaults, and both defaults were wrong in a
way only a caller sees.

zip derives an unstated id from the path, so the four ops published
`get_v1_engine_status`, `get_v1_engine_models`, `get_v1_engine_model`,
`get_v1_engine_system` — path mangling where the rest of the fleet publishes the
product and the noun. The risk product's thirty-one operations are `riskScore`,
`riskState`, `riskDatasets`, `riskLabelCoverage`; these are now `engineStatus`,
`engineModels`, `engineModel`, `engineSystem`, which is also the rule this
package already applied to its own SCHEMA names and only to those.

A summary defaults to the first sentence of the Go doc comment, and a Go doc
comment opens with the Go IDENTIFIER — so the published summaries read "Status
reports whether the engine deployment is reachable", "Models lists the models
the engine serves", "Model reads one model's load state". A Go symbol name was
the first word a CLI user, an SDK reader and a model picking a tool saw. Each op
now states a summary written in the imperative for the person calling it, and
the doc comment stays a Go doc comment that zipdoc still lifts as the
description: two audiences, two sentences, one declaration.

FORWARDS-ONLY, and it costs nothing: this plane has no customers on it. No
alias, no redirect, no compat shim.

Regenerating from source changes exactly four operation ids. No path is added or
removed, no (path, method) pair moves, no schema changes, and openapi/floor.json
is byte-identical because the operation count did not move.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 00:48:16 -07:00
hanzo-dev 3b50091c77 mcp: the catalogue is a query — delete the 116 committed tool files
The fleet's agent door answered from plugin/<app>/mcp.json: the tool array each
app's binary projected when it was BUILT, embedded by plugin/embed.go and handed
to zip as Plugin.Tools. 116 files, 49,865 lines, and a second source for a fact
every child already knows.

A second source can only be stale or accidentally correct. This one was stale in
the way no gate in this repository could see: o11y's 353 missing ops live in
github.com/hanzoai/o11y, so a go.mod bump in ANOTHER repo invalidated an artifact
in this one with nothing in the diff to say so. Regenerating it more often is not
the fix — a generator on a hook is still two sources with a race between them,
and the trigger is in a different repository. (593aa309 did regenerate it, which
is why the file reads 365 today. The next cross-repo bump silently un-fixes it.)

So the host asks. POST /v1/mcp is the HOST's own handler now (zip's is Disabled,
so exactly one handler holds the address). A tools/list forwards the CALLER's own
message to every composed subsystem's own /mcp over its private ZAP socket, in
parallel, and unions the replies — zip.App.Start resolves a cold child on the
same single-flighted path a prefix request takes, so the first list pays one
start per app and nothing after it does. A tools/call goes to the app that listed
the name, verbatim; the child's own registry decides whether the tool exists.

A SUBSYSTEM THAT DOES NOT ANSWER IS NAMED, in result._meta["hanzo.ai/unavailable"],
because a silently-short list and a stale file are the same defect: the caller
cannot tell an app that serves nothing from one that did not answer. Measured on
the built binaries — host + real o11y child, kms pointed at a dead address:

    tools=364, unavailable=[{kms, connection refused}]

364 and not 365 because o11y projects get_v1_o11y_logs twice; the door serves the
first and logs the collision. THAT DUPLICATE IS WHY cmd/cloud's tests were red on
main — zip refused the Load ("tool is already served by plugin o11y"), a boot
failure. One name still has one owner; it is no longer fatal to the fleet.

Also gone with the mechanism they configured: manifest.App.Open and zip's
one-open-plugin rule. The host forwards the caller's own request to EVERY
subsystem now, so each answers for this caller out of its own rows, and being
asked per caller is no longer a privilege one app holds.

The release gate moved with the door. Car 3 compared the live tool count against
`jq -s length` over the committed files — both sides were the same bytes, so it
proved only that the image carried its own tree, and it passed while o11y's
catalogue held 12 of 365. It asks the better question now: did every subsystem
answer. A broken deployment used to match the files exactly.

plugin/<app>/openapi.json SURVIVES, for the one reason the catalogue could not:
the weave carries each subsystem's prose, and that prose is lifted from the app's
SOURCE at describe time (openapi.Synopsis). A running child has no comment to
read and would answer with its deployment's brand blurb, which the weave would
publish as the description of every product tag. Deleting that half waits on the
synopsis becoming a declared value.

Tests are against RUNNING subsystems (fleet/mcp_test.go): real zip children on
real ZAP sockets, exact sets, bodies never status codes. Mutation-checked three
ways — unmount a child, silence the outage report, ask only the first app — all
three go red.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-02 14:22:10 -07:00
hanzo-dev f403c603f1 cloud: the composition root's verb is Listen, because that is what it does
CI/CD / gate (push) Canceled after 0s
CI/CD / containment (push) Canceled after 0s
CI/CD / image (push) Canceled after 0s
CI/CD / rollout (push) Canceled after 0s
CI/CD / reach (push) Canceled after 0s
CI/CD / fanout (push) Canceled after 0s
CI/CD / receipt (push) Canceled after 0s
Serve and Listen were two names for one act. zip's App already calls it Listen —
`app.Listen(zapAddr, httpAddr)` — and this function's whole job is to build that
app and hand it its addresses, so calling it Serve made the entry point disagree
with the thing it enters. One verb, all the way down: a plugin's main says
cloud.Listen, cloud says app.Listen, and nothing has to be translated in a
reader's head on the way through.

117 composition roots move with it. ServePlane is untouched — it names a
different act (bind one app's own socket for the internal plane), and collapsing
it into this would be the opposite of the point.

Also fixes apps/iam's TestMain, which had gone red on every store test:
credz.Boot's last resort is cek.EnsureDevKey, and that DECLINES on a codec-linked
build by design — a build that can really encrypt must be handed a real key, not
invent one. So the throwaway goes in through the same door a deployment uses, and
only when nothing else supplied one. Six failures back to the one pre-existing
ratchet (iam serves 97 untyped ops against a budget of 88).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-02 09:53:56 -07:00
hanzo-devandzeekay 7e3bb4af9b describe: regenerate the woven document so the prose fix reaches the wire
`make describe` — zipdoc lifts the doc comments, each app projects its own
router, the weave composes them. Path count is unchanged at 1058: this
moves prose, not surface.

It also picks up drift nobody had regenerated: apps/git's zipdoc_gen.go was
missing the import/inbound/mirror ops' prose entirely, and apps/admin's had
gone stale. Both were invisible because the weave only proves the subsets
compose with each other — the drift gate is what regenerates from source.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-01 10:25:03 -07:00
hanzo-dev fe782d1bec docs: the last seven openers, and none of them was about the customer
These are the remainder of the rule 668c63ac, 55ef5731 and 4cfc10d0 established:
openapi/synopsis.go lifts a package doc's FIRST SENTENCE verbatim into that app's
own subset as info.description, the weave lands it on the product tag, and from
there it is the MCP tool prose an agent reads and the line `hanzo <product>
--help` prints. A sentence that opens "is the ... plane", names a /v1 path, or
points at apps/pubsub answers a question no buyer asked.

Six of the seven are products revived AFTER those passes ran — 9640ea2e mq,
6db519e9 pubsub, a31e0ec9 flow, eafc8a82 engine, 48004de8 registry, 34663c49
auto — so their docs were written to the register the rule replaced. share was
simply missed. (analytics was the eighth and is not here: 5a781b8a landed it
from another lane while this was building, and its opener already holds the
rule, so this takes that one rather than restating it.)

  auto      "…executed as durable runs on the hanzo tasks plane"
            → Hanzo Auto: build a flow from triggers and actions, publish it,
              and watch every run.
  engine    "…what it serves and what it runs on, read through /v1/engine"
            → Hanzo Engine: which models the serving runtime has loaded, and
              the GPUs under it.
  flow      "…run agent workflows on the unified /v1 plane"
            → Hanzo Flow: build an agent workflow on a visual canvas, run it,
              and read every run.
  mq        "…served at /v1/mq over the broker apps/pubsub embeds"
            → queue and stream admin for your org: create them, watch them
              drain, ack what you pulled.
  pubsub    "…served to tenants at /v1/pubsub over the embedded Hanzo PubSub
            (NATS + JetStream) node this same package runs"
            → your message bus: publish, subscribe, and durable streams your
              apps read at their own pace.
  registry  "the management plane over the platform's artifact registries…on
            the unified /v1 plane"
            → your container and package registry: push images, pull them back,
              see what you store.
  share     "…folded into the ONE cloud binary"
            → a public URL for a service on your own machine, and a list of
              what you have open.

NOTHING IS DELETED. Every clause the old openers carried — the tasks plane, the
/v1 mounts, the broker apps/pubsub embeds, the JetStream node this package runs,
the management-plane framing, the ONE binary — is still in the doc, one sentence
lower, where the engineer it was written for still finds it. Only the ORDER
changed: what the product is, then how it is built.

Each opener now clears the whole rule: ≤90 characters after the "Package <name>
is" the projections strip (81–91); no mounts/surface/subsystem/plane/binary; no
/v1 path, file name, app reference or HIP number; no shouting. And each ends
sentence one with a period on its own paragraph, so go/doc's Synopsis publishes
the SENTENCE and not the paragraph — the failure mode o11y hit.

That is the last of them: all 91 described tags now lead with the product.

apps/share/client.go was already gofmt-dirty on main (struct tag alignment in the
zrok overview response) and is now clean, since it is a file this touches.

Regenerated FROM SOURCE, not hand-edited. The artifact diff is seven description
lines — 1058 paths, byte-identical to main; no path, operation, schema or tool
name moves.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-31 09:28:57 -07:00
hanzo-dev eafc8a8267 engine: revive the product at /v1/engine — typed passthrough to the hanzoai/engine runtime
The product repo (hanzoai/engine, Rust — `hanzo serve`, the OpenAI- and
Anthropic-compatible inference server) stays the one implementation; cloud
mounts its MANAGEMENT plane over an HTTP seam, apps/flow's posture: four
typed ops proxying what the server genuinely answers today — the model table
with load state, one model's state (a GET here over the product's POST-read
seam, since its ids carry slashes), the host/GPU inventory with build
capabilities, and a reachability lens carrying the build revision — each
proven against a live hanzo-server end to end (live_test.go re-proves the
loop on demand against a real serving process; the fake upstream pins the
measured wire, plain-text /health included).

Inference is deliberately NOT here: the fleet's ONE metered inference door is
the OpenAI-compatible /v1 surface (apps/ai + the zen claim); a second
completion door under /v1/engine would split billing, and the ledger pins it.

The deployment is ONE shared runtime with no per-org primitive, so every read
is a platform fact behind the IAM gate (validated principal or 403, before
any upstream byte) and every mutation the server exposes (models/unload,
reload, tune, re_isq, system/doctor) is REFUSED: an org-scoped route onto a
shared runtime hands each tenant every other tenant's availability.

The rest of the 22-path authored intent (deleted unserved in openapi d86248f)
stays refused: clusters/jobs/ray/pipelines/gpus/serve-endpoints described a
GPU cluster manager this product never was — those families live on the
cluster plane (/v1/clusters, /v1/train/jobs, /v1/ml/models) where they are
real. The ledger is a measured gate (typed_wire_test.go intentRefused), not
a comment.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-30 22:48:22 -07:00