9 Commits
Author SHA1 Message Date
zooqueenandHanzo Dev 53905542ff The gate holds on amd64, and both images release as 1.5.89
The gate calls hanzoai/ci [email protected] with linux-amd64 as the only
architecture that gates, and linux-arm64 as an advisory leg that runs and
reports without holding anything. The o11y pod is scheduled on amd64, and the
one shared arm64 node kept the release queued for hours behind other repos.

hanzo.yml states version 1.5.89. The two images' series had drifted apart
(o11y at 1.5.88, o11y-site at 1.5.82) and far ahead of the git tags (v1.5.75), so
a derived number differed per image and a git tag would have overwritten an
August image of the same name. imgver publishes the declared version once and
the next patch thereafter, for both images alike.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-24 10:53:08 -07:00
zeekayandHanzo Dev 5103e1151e fix: call the pipeline at the address it actually lives
hanzoai/ci keeps the reusable at .github/workflows/build.yml — the one path
both planes resolve, since github.com reads a reusable only from there and the
forge reads it too. The v1 tag moved with it, so a caller still naming
.hanzo/workflows/build.yml asks for a blob that is not in the tree:

  InsertRun: read hanzoai/ci@v1:.hanzo/workflows/build.yml: object does not exist

The caller itself stays at .hanzo/workflows/cicd.yml on purpose: github.com has
no runner for these labels, so a caller placed there could never be scheduled.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 01:14:30 -07:00
zeekayandHanzo Dev 223e80a932 The gate and the image are one lane
Four workflows with no `needs:` between them cannot order themselves, so
the gate and the publish raced instead. A red gate published anyway:
v1.5.61, v1.5.62 and v1.5.63 were each built and pushed on a commit whose
gate had already failed on that same commit, and production ran v1.5.62.

  v1.5.61  b217afa  gate 35417 FAILURE   image 35419 success
  v1.5.62  ceeb972  gate 35624 FAILURE   image 35625 success   <- shipped
  v1.5.63  fac2358  gate 46116 FAILURE   image 46117 success

`images:` moves into hanzo.yml so the shared lane's single sequential job
runs `test:` and only then builds -- one job, one order, nothing to wire.
Adding `needs:` across four workflows would have been a second mechanism
to keep in step.

The blocker this file recorded was stale: the shared lane already mounts
the `gh_token` BuildKit secret the Dockerfile needs for the private
hanzoai/sqlite and hanzoai/datastore-go modules, from KMS with the org
GH_PAT as fallback. Version metadata keeps arriving too, via the lane's
`args:` placeholders, so the binary still names its own release rather
than reporting its ARG default.

ci.yaml goes with them. It ran the same two commands as `test:`, and
checked out hanzoai/cloud at a pinned SHA for a
`replace github.com/hanzoai/cloud => ../cloud` that go.mod no longer has
-- a second gate that can go red for a reason unrelated to this repo is
how a fleet learns to ignore red.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-14 09:31:50 -07:00
zeekayandHanzo Dev 840c2203a9 ci: canonical pair — hanzo.yml + forge caller
github.com has no runner for hanzo-build-linux-amd64, so a caller under
.github/workflows is a gate that can never be scheduled. This is the ~7-line
caller on the plane that can (git.hanzo.ai git-runner fleet), pinned @v1.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 02:24:21 -07:00
zeekayandHanzo Dev 55b7918b7f mount: the route table takes the router, and the binary links it
Mount(app *zip.App, deps cloud.Deps) fused two separate things — WHAT the 367
routes are, and WHICH host's dependencies serve them. It read exactly one field
of that second argument, deps.Logger, on exactly one line, to announce itself.

That one line cost three things, each worse than the last:

  - github.com/hanzoai/o11y required github.com/hanzoai/cloud, which requires
    github.com/hanzoai/o11y. A module CYCLE, for a log line.
  - Because the cycle made a whole-module resolve impossible in an image with no
    cloud checkout, the Dockerfile scoped itself to one package to route around
    it — and wrote the workaround down as if it were a design decision.
  - So cmd/community, the binary the image runs, could not import the route
    declarations without dragging the embedding host in. It never imported them.
    353 typed ops, 353 published operations and 353 MCP tools were true of the
    source and absent from the process.

The last one is the P0 and it was invisible to every proof the conversion had:
the tests mount the table directly inside the test process, which is exactly the
thing that cannot fail. An uncalled package-level func is legal Go — the package
built, the route arithmetic added up, and the shipped server published nothing.
Status codes could not see it either. /version answers 200 either way, because
the service's own registration answers it. The tell was the body of an UNKNOWN
path: text/plain "404 page not found", which is pkg/web/noopweb's http.NotFound
behind the console's terminal catch-all.

  A ROUTE TABLE IS A VALUE. It takes the router it registers into, and nothing
  else. The router already carries the host's logger — app.Logger() IS the value
  Deps.Logger was — so the second argument never carried information the first
  did not already have.

Mount(app *zip.App) error. cloud is gone from go.mod, and ~20 transitive
requirements go with it (hanzoai/{iam,commerce,authz,tasks,vfs,orm,ha,s3-go,…}).
Standing proof, and it must stay this way:

  go list -deps ./cmd/community | grep hanzoai/cloud            # EMPTY
  go list -deps ./cmd/community | grep -x github.com/hanzoai/o11y  # PRESENT

The second line is the one that matters. pkg/query-service/app's new publish()
mounts the ONE table onto the router the server serves, then calls app.Prepare()
— which is what turns the registry into doors: /.well-known/openapi.json, /docs,
/mcp, the by-name call plane. This server serves through Fiber().Listener rather
than zip's own Listen, so without that call the document would exist in the
process and answer on no port.

Order is the contract, twice. AFTER the service's own routes: Fiber matches in
registration order, so every path is still answered by the handler that has
always answered it — no relay hop, no buffered round-trip, and therefore
livetail, the long-poll and the chunked export still stream on this listener. The
table NAMES the surface here; it does not stand in front of it. BEFORE the
console's terminal catch-all, or the SPA would answer for Prepare's routes.

Three tests read this out of the router instead of asserting it about the source:
the served document publishes 353 operations as application/json, every path in
it is o11y's own surface, and an earlier registration outranks the declaration.

hanzoai/cloud keeps working with a mechanical call-site change —
o11y.Mount(a, deps) -> o11y.Mount(a) at apps/o11y/o11y.go — and cmd/cloud builds
clean against this tree. That change lands in cloud with its next o11y pin bump;
cloud's current pin (v1.5.41) is unaffected.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-01 20:04:12 -07:00
zeekayandHanzo Dev 93785eb98b ci: registry cache — the gha backend cannot work on this forge
The ghcr login was fixed and the build still failed, one step later:

    ::warning::Cannot parse GitHub Actions Runtime Token: missing part #2
    ERROR: failed to solve: failed to configure gha cache exporter:
           token is malformed: token contains an invalid number of segments

`type=gha` resolves its backend from an Actions runtime token, and git.hanzo.ai
does not issue one buildx can parse. Because the exporter is configured as part
of the solve, that does not degrade to an uncached build -- it fails the BUILD.
A cache meant to save time was costing the entire job. Same family as
upload-artifact v4+, which the forge also refuses.

Registry cache instead, at the image each workflow already pushes, so no new
credential and no new home. ignore-error=true on both ends: the first build has
no cache image yet, and a 404 there must not be fatal -- that is the second
failure visible in luxfi/dex today.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-01 18:09:51 -07:00
zeekayandHanzo Dev ec1bc6b2c8 ci: name the ghcr credential instead of falling through to it
Both image workflows logged in with `secrets.GH_PAT || secrets.GITHUB_TOKEN`.
That resolves today only because orgs/hanzoai happens to provision GH_PAT -- an
org secret that is absent evaluates to "", so the chain silently swaps to the
git.hanzo.ai token, which ghcr.io rejects with `denied: denied`. The log never
says which credential was used, so the failure reads as a registry problem; that
is how the same defect in luxfi/trader stayed misdiagnosed until the token was
tested directly against the registry.

GHCR_USER + GHCR_TOKEN, named directly. The build secret below is untouched:
`gh_token=${{ secrets.GH_PAT }}` is the credential the Dockerfile mounts to
fetch private hanzoai modules, which is a different job from logging in.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-01 16:47:17 -07:00
zeekayandHanzo Dev d78ae42e2c openapi: one emitter, not two — the typed registry is the document
o11y wrote the same server down twice. Emitter A is the typed op registry:
identity 45 + infra 44 + logs 9 + telemetry 5 = 103 ops registered on the
cloud.Router at their full public /v1/o11y/... paths, read from a real mount by
cloud's describe.go and woven into the one published document.

Emitter B was `o11y generate openapi`: a swaggest reflector walked the runtime's
gorilla/mux tree and wrote a committed 629 KB docs/api/openapi.yml — 120 paths,
174 ops, spelled /api/v1 x59, /api/v2 x57, /api/v3, /api/v4, /api/v5 x2, and
ZERO /v1/o11y. It described the fork's internal tree behind the delegation
wildcard's /v1/o11y/* -> /api/* rewrite: a surface no customer can address, in a
shape that breaks two house rules on its face (/api/, and v2 through v5). No CI
step regenerated or verified it, so it could only drift.

B dies whole: cmd/openapi.go, registerGenerateOpenAPI, pkg/o11y/openapi.go (the
reflector, unreachable once the command went) and the committed artifact.
pkg/http/handler's OpenAPIDef stays — it is per-handler prose the typed ops read,
not a second document.

Two scratch leaks from the same effort go with it:

  - zzz_surface_measure_test.go, self-labelled "TEMPORARY … not for commit",
    committed anyway and writing to a hardcoded /home/z/.cache path.
  - replace github.com/hanzoai/cloud => ../cloud. No CI runner has that sibling,
    so ci.yaml carried a second checkout of cloud at an exact commit to supply
    it — the same fact in two places, which is what LLM.md's own section warned
    about. The replace hid a broken pin, too: the required v1.801.218 no longer
    compiles against current hanzoai/tasks, and nothing noticed because CI never
    built the required version. go.mod now requires cloud v1.801.350 from the
    proxy like every other private hanzoai module; the checkout step is gone.

go build ./... clean; go test ./ ./cmd/... ./pkg/http/handler/... ./pkg/apiserver/...
./pkg/o11y/... all ok.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-01 11:20:15 -07:00
zeekayandHanzo Dev 01d0424ca9 ci: restore the build lanes at .hanzo/workflows
The three workflows that build this repo were deleted from .github/workflows
without a replacement anywhere, so the promoted o11y-site App had nothing left
that could build its image — and no red run to show it, because a workflow that
does not exist cannot fail. Restored byte-for-byte from history at the path the
forge reads:

  ci.yaml           go build + go test, with the pinned cloud sibling
  docker.yaml       ghcr.io/hanzoai/o11y        (headless server)
  docker-site.yaml  ghcr.io/hanzoai/o11y-site   (SPA, pinned by crs/o11y-site.yaml)

runs-on is untouched — the forge's runner answers the same labels. The only edit
is docker-site.yaml's paths filter, which pointed at its own old location and
would never have matched again.

release-drafter.yml is not restored: it drafts GitHub releases off an upstream
SigNoz config, which means nothing once GitHub is a mirror.

.github/workflows now holds exactly one file, sync.yml, replaced with the
canonical copy — the variant here predated it and drifted.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 08:37:54 -07:00