title() answers a brand's title or a Hanzo product's own name: PRODUCTS
holds the one product that has one, team → "Hanzo Team" (hanzo.team),
so @hanzo/app's app({ brand }) reads a name from here instead of keeping
its own brand() and wordmark(). An id that names neither throws.
The package publishes from CI now. Its gate could not pass on a fresh
checkout — pnpm gen read @hanzo/tokens from a sibling ../ui checkout, and
the declaration build died on TypeScript 6's baseUrl deprecation — so it
went out from a laptop. @hanzo/tokens is a dev dependency, tsconfig states
its types and the deprecation, and publish.yml reads the npm token from
KMS. styles/variables.css is regenerated from @hanzo/tokens 1.1.1, which
is what every build already wrote.
deploy.yml ran buildx on a runner with no Docker daemon, so it never
published. hanzo.yml now declares the image and cicd.yml hands it to the
build door.
Claude-Session: https://claude.ai/code/session_01HuMDfgFssPUptYUzgZ6Cp8
533b0ce changed styles/variables.css under the version that already named the
Geist sheet, so 1.4.9 meant two different files and hanzoai/ci, which pins the
bytes by hash, could no longer say which one it vendors.
Co-authored-by: Hanzo Dev <[email protected]>
typescript 7.0.2 is the native compiler and exports no JavaScript API, so
tsup's --dts build and eslint cannot read it. It comes in aliased as tsgo
and does the typechecking; typescript stays on the 6 line for the tools.
8 diagnostics before and after (pre-existing @types/node gaps in src/cli.ts).
Co-authored-by: Hanzo Dev <[email protected]>
The package goes out from the release script, not from here: this
file has never produced a run on either plane.
Co-authored-by: Hanzo Dev <[email protected]>
ghcr.io/hanzoai/static served only linux/amd64 from v0.5.1 through 0.5.14,
so an image built on it has no manifest an arm64 node can pull. 0.5.15 is
the same server source published as an index over both architectures.
Co-authored-by: Hanzo Dev <[email protected]>
The forge reads .github/workflows natively and github.com reads nothing else,
so one directory serves both planes instead of two conventions that disagree
about which one is live. The reusable in hanzoai/ci already lives there, so a
caller naming that path now resolves to a file that exists.
Co-authored-by: Hanzo Dev <[email protected]>
The primary-network chains answer to either case; one spelling beats two.
/v1/chain/c/rpc, /v1/chain/p, /v1/chain/x across the estate.
Co-authored-by: Hanzo Dev <[email protected]>
hanzo-build-linux-amd64, lux-build-linux-amd64, zoo-build-linux-amd64 and
every other org-prefixed spelling all meant the same machine. Which org a
job belongs to does not change which host can build it, so the org bought
one pool name per org per arch and each could go stale on its own -- the
evo host alone answered to fourteen names for one label.
One vocabulary now: linux-amd64, linux-arm64, macos-arm64, win-amd64. The
generic labels every plain workflow uses (ubuntu-latest, self-hosted) are
untouched, so naive CI keeps working.
Co-authored-by: Hanzo Dev <[email protected]>
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]>
One family. Font stacks name Zen and fall back to the native faces, tokens
name the role rather than a face, and the packages that used to supply a
face now point at @hanzo/font.
Self-hosted @font-face blocks are removed rather than renamed: rewriting the
family in one would leave a src pointing at a file this repo no longer ships.
@hanzo/font carries the binaries and declares the faces, so the local copies
go with the declaration that used them.
Comments naming a face we no longer use are deleted, not reworded.
Picks up the graded-ink value from @hanzo/tokens so the sheet the Go dashboards
vendor stays identical to @hanzo/design's live palette.
Co-authored-by: Hanzo Dev <[email protected]>
variables.css was a hand-authored palette that had drifted from the system — a
purple accent, a Zen type stack, blue/amber status hues, its own surface blacks.
It is now @hanzo/tokens' generated bundle, re-emitted by scripts/gen-variables.mjs,
so the Go dashboards that vendor this sheet (ci/cd/git) share the one monochrome
palette. A short compatibility block aliases the legacy names those consumers
still reference (--hanzo-accent -> the ink, --error/--success -> the state hues,
--border-hairline -> --border, ...) onto the generated tokens, so nothing that
links this sheet stops resolving. Edit @hanzo/tokens src/theme.ts and run `pnpm gen`.
Co-authored-by: Hanzo Dev <[email protected]>
luxd serves chain APIs under one segment from constants.ChainAliasPrefix
(constants v1.6.4, node v1.36.181):
/v1/bc/<alias>/... -> /v1/chain/<alias>/...
/ext/bc/<alias>/... -> /v1/chain/<alias>/...
Observations keep their original addresses: a dated note or a recorded status
code is a fact about when it was written, not an instruction to follow.
A version written into a manifest is written into every manifest that wants it,
which is how eslint came to exist in a hundred and four forms across this estate
and how a package came to resolve twice at run time. The catalog moves the number
out of the manifest: a manifest names the package, the workspace names the
version.
Four are held below their latest because the newer release cannot be used, and
the reason is recorded rather than the version alone: @noble/hashes 2 renamed
every subpath the web3 stack imports, @splinetool/runtime 2 depends on a package
that was never published, @dynamic-labs 5.4.1 the same, and react-resizable-panels
3 points its types condition at the bundle.
Every dependency on a @hanzo/@luxfi/@zooai package now names the same version
across the repo: a catalog entry where pnpm supports one, an exact pin where it
does not, and a range only where a peer genuinely means one.
pnpm itself is pinned too — the estate ran eighteen different versions, five of
them too old to read a catalog.
runs-on was a bare scalar. Jobs written that way queue and are never
picked up — link and quest have waited since 10 August, painter since the
5th, none of them with a single success — while repos writing the same
label as a sequence drain within seconds of a push on the same fleet.
Same label, same runners, different shape. It fails silently: nothing errors,
the job simply never starts, so the lane looks configured and does nothing.
Three surfaces still asked for a face this brand does not own. `loadFonts`
injected a Google Fonts stylesheet for Inter at runtime — a third-party
fetch for a family nothing here declares, and dead in the estate besides;
@hanzo/font is where a face comes from. The showcase page labelled its own
type specimens "Inter Bold" and "Inter Regular", and the hero card led its
stack with Inter. The comments in typography.ts narrated the old defect
instead of describing the code.
Every logoUrl and faviconUrl floated on `@latest`, which resolves when the
browser asks: the bytes behind a login page could change with no release here,
and nothing recorded which version a visitor got. jsDelivr also keeps serving a
path from a version that no longer ships it — @luxfi/brand@latest answers
assets/logo/logo.svg with a 120-byte triangle that no explicit version of that
package contains, because every version from 1.0.7 dropped assets/ entirely.
Lux publishes its mark in @luxfi/logo, which its own brand.json already names,
so that is where this points now: svg/lux-icon-white.svg and svg/lux-favicon.svg
at 1.0.8, both verified to serve. Hanzo pins 1.4.7 and Zoo 1.4.1, likewise
verified. @parsdao/brand ships no assets/ at any version, so its URL is a 404
either way; pinning it names the gap rather than leaving an alias to hide it.
A test walks the registry and refuses a URL that omits its version.
Co-authored-by: Hanzo Dev <[email protected]>
typography.ts named Geist Sans and Geist Mono in three stacks and two code
styles, and shipped a `fontFaces` string declaring @font-face against
/fonts/geist-sans-var.woff2 and /fonts/geist-mono-var.woff2 — files this package
has never contained, so it could only ever paint the fallback. Nothing imported
it. Deleted rather than repointed: the faces belong to @hanzo/font, which ships
Zen, Zen Mono and the five Zen Pixel variants with the rules to match, and a
second declaration is how two of them come to disagree.
The SVG card renderers each declared the same font stack, both leading with
Inter — a face this brand has never owned. One `svgFontStack`, imported by both,
leading with Zen. The tail is unchanged, so a rasterizer that cannot resolve Zen
falls through exactly as before and this cannot render worse than what it
replaced; point resvg-js at @hanzo/font to get the real face.
type-check + build green, 20/20 tests pass.
Co-authored-by: Hanzo Dev <[email protected]>
`BrandContract` said the same word twice: every type is a contract, so the suffix
carried no information a reader did not already have from the name and the
`interface` keyword. The pair it belongs to says what each half is on its own --
`BrandIdentity` is the authored registry entry, `Brand` is what a consumer holds --
and `toBrand` names the projection between them.
Nothing outside this package and @hanzo/id referred to either name, so this is a
rename and not a migration.
The host-impersonation cases held literal NUL bytes, which made the test
file binary. grep reports no matches in a binary file without saying it
skipped it, so the suite proving we reject null-byte host spoofing was
itself invisible to any search of this repo. Same characters, same
assertions.
Co-authored-by: Hanzo Dev <[email protected]>
LLM.md is the source and stays the source; CLAUDE.md is a symlink to it, so there is
one file and it cannot drift from itself. What changes is that the link is committed
rather than left for each checkout to recreate.
Left untracked, the doc was conditional on a step outside the repo: a fresh clone had
no CLAUDE.md, nothing loaded LLM.md, and an agent worked here having never read what
this repo already explains. Most of the estate already tracks theirs; this is the rest
agreeing with them.
BrandContract had logoUrl and faviconUrl and nothing for a logotype, so a brand
with both had nowhere to put the second one. @luxfi/brand carried it anyway, off
contract, and pointed logoUrl at it as well — which is how asking for the Lux logo
returned a 63x17 wordmark to every surface that wanted the triangle.
Optional, because most brands have no wordmark: a consumer that wants a logotype
and will settle for the mark reads wordmarkUrl ?? logoUrl.
Co-authored-by: Hanzo Dev <[email protected]>
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]>
point the registry at our verdaccio, which proxies and caches all of npm
alongside our own scoped packages. lockfiles are untouched: npm and pnpm
both honor .npmrc at fetch time, so no re-resolution is needed.
Co-authored-by: Hanzo Dev <[email protected]>
The deploy job asked for `ubuntu-latest`. This file lives in .hanzo/workflows,
which GitHub never reads -- git.hanzo.ai is the only thing that can run it, and
the fleet advertises no generic ubuntu-* label. That is deliberate and recorded
in the runner ConfigMap: ~1400 mirrored upstream forks all ask for ubuntu-latest,
and the forge errors rather than skipping a job it cannot place, so one bad job
was retried ~520 times across 10 runners.
An unmatched label is not a failure here, it is silence -- the job queues until
the 24h timeout. So this deploy has never produced an image, and nothing ever
said so. Same class as the playground regression, found by checking every
.hanzo/workflows file against the labels the runner ConfigMap actually declares
rather than against the ones I assumed.
hanzo-build-linux-amd64 is registered (verified against cm/git-runner-config).
Co-authored-by: Hanzo Dev <[email protected]>
Hanzo's identity is written in three places and all three disagreed:
brand.json said x.com/hanaboroshi, the registry said x.com/hanzoai, and
brandConfig still pointed at the dead twitter.com domain. It is hanzoai, which
is also what every sibling does — luxdefi, zoo_labs, parsnetwork.
The consistency test pinned six fields and none of the socials, so this drifted
in silence. It now pins twitter and github across brand.json, the registry AND
brandConfig, and asserts the host is x.com. Verified it catches the old value:
restoring hanaboroshi fails two tests, exit 1.
Folded into the unpublished 1.4.5 rather than bumping again.
Co-authored-by: Hanzo Dev <[email protected]>
Every subpath put "types" after "import" and "require", where the resolver
never reaches it — 12 esbuild warnings a build. Types still resolved, but by
adjacency: TypeScript found dist/registry.d.mts next to dist/registry.mjs on its
own, so the exports map was decoration and tsup's output naming was the real
contract.
Named explicitly instead, and per condition, which the dual build has always
supported: import -> .d.mts, require -> .d.ts. All 24 targets verified present,
and a node16 consumer typechecks against brand-id, registry and marks.
Co-authored-by: Hanzo Dev <[email protected]>
Added it on the theory that the forge was drifting. It is not: the forge copy
matched GitHub at every check, because this repo is a pull-mirror and Gitea's
own scheduler keeps it current. The job would have been a second mechanism
doing what the first already does, pushing refs into a repo the server owns.
sync-from-github belongs to the NATIVE forge repos — cloud, ci, mirrors — which
have no mirror behind them. The real reason nothing ran here was the Actions
unit, off since migrate; hanzoai/mirrors reconcile.py now switches it on for any
repo carrying .hanzo/workflows.
Co-authored-by: Hanzo Dev <[email protected]>
publish still ran npm ci, which cannot work since 11ea83d moved the repo to
pnpm and deleted package-lock.json — the next version bump would have failed at
install. It installs with pnpm now and gates on pnpm test, which builds dist/
and then runs the suite.
sync-from-github pulls the mirror every 10m, fast-forward only. Nothing carried
refs here after the GitHub-side push was deleted.
Co-authored-by: Hanzo Dev <[email protected]>
luxd serves exactly one HTTP prefix and it is /v1. Measured 2026-07-27:
api.lux.network/ext/bc/C/rpc -> 404 (5/5 probes), /v1/bc/C/rpc -> 200
chainId 0x17871. So these callers were already broken, not merely legacy.
The bc/ segment is required (/v1/C/rpc is malformed and 404s), the env is
the hostname and never a path segment, and the brand is the hostname and
never a chain alias -- api.lux.network/v1/bc/hanzo/rpc is 404 now that each
L1 has its own sovereign gateway.
api.avax.network is deliberately untouched: it is a third-party host that
serves /ext/ (200) and not /v1/ (404). Rewriting it would break Avalanche.
Co-authored-by: Hanzo Dev <[email protected]>
TypeScript 7 is the native Go compiler and removes `baseUrl` and
`moduleResolution: node|node10`. Both appear here, so `tsc` from TS7
refuses the config outright (TS5102 / TS5108) and cannot typecheck.
`paths` targets resolve relative to `baseUrl` when it is set and relative
to the tsconfig file otherwise. Every `baseUrl` folded here already
pointed at the config's own directory, so dropping it moves nothing and
the targets are left byte-identical. Where a baseUrl pointed elsewhere,
each affected target was rewritten as join(baseUrl, target).
`moduleResolution` was chosen from the declared `module`: commonjs ->
node16, esnext/preserve -> bundler. Configs whose `module` is unset or
exotic were left alone rather than guessed at.
The result is accepted by BOTH toolchains, so nothing has to upgrade
TypeScript in lockstep. Verified on hanzo/chat packages/api: tsc 5.9
779 -> 778 errors (no regression), and tsc 7.0.2 now runs the project
in 2s where it previously refused the config.
Co-authored-by: Hanzo Dev <[email protected]>
Superseded: the Hanzo GitHub App pushes a webhook, so the forge tracks GitHub
without a per-repo workflow. This file called git.hanzo.ai/api/v1/.../mirror-sync
— a Gitea API for a system we no longer drive — and would sit inert in every repo.
One mechanism, in one place, instead of ~350 copies of a cron.
One package manager, one lockfile. pnpm-lock.yaml is tracked, package-lock.json is
deleted and every other manager's is ignored, and `packageManager` pins the version
corepack installs. pnpm-workspace.yaml names the packages allowed to run install
scripts; pnpm denies the rest by default.
Co-authored-by: Hanzo Dev <[email protected]>
The npm publisher was deleted this morning without a replacement, so
@hanzo/brand had no producer anywhere: no workflow, no failing run, just a
package that would stop moving. It is back, byte-identical, at
.hanzo/workflows/publish.yml where the forge reads it.
.github/workflows now holds the canonical sync.yml alone — refs to
git.hanzo.ai and nothing else. The file it replaces asked Gitea to pull, which
only makes sense while GitHub is upstream.
The site follows the same shape as every other Hanzo surface: index.html is
self-contained, so the Dockerfile copies it into ghcr.io/hanzoai/static with no
build stage, and deploy.yml publishes ghcr.io/hanzoai/brand:<sha> without
deploying it. hanzoai/universe crs/brand.yaml names the tag that serves and is
inert until a human pins one. Replaces the GitHub Pages push that Pages never
built — brand.hanzo.ai has answered 404 from our own ingress the whole time.
Co-authored-by: Hanzo Dev <[email protected]>
git.hanzo.ai mirrors this repo by PULL on a ~10-minute interval, and arcd runs
CI/CD there — so every push waited out that interval before anything built.
This asks Gitea to pull HEAD immediately.
Latency only: the repo already mirrors via the App webhook, so a missing
HANZO_GIT_TOKEN or a failed curl is non-fatal and never fails the push.
Idempotent (mirror-sync just pulls HEAD) and concurrency-coalesced.
This workflow ran on a self-hosted actions-runner-controller pool. ARC is
decommissioned: CI executes inside git.hanzo.ai (Gitea Actions, .hanzo/workflows)
and delivery is cd.hanzo.ai reconciling the reviewed image pin in
hanzoai/universe. GitHub is a mirror and runs nothing.
Removed rather than repointed — there is no GitHub-side pool to move to, and
leaving it would keep ARC load-bearing.
This workflow ran on a self-hosted actions-runner-controller pool. ARC is
decommissioned: CI executes inside git.hanzo.ai (Gitea Actions, .hanzo/workflows)
and delivery is cd.hanzo.ai reconciling the reviewed image pin in
hanzoai/universe. GitHub is a mirror and runs nothing.
Removed rather than repointed — there is no GitHub-side pool to move to, and
leaving it would keep ARC load-bearing.
@hanzo/brand is the ONE dynamic brand-settings source every surface reads. Retune
the default --font-size-* scale (styles/variables.css + typography.ts, kept in
mirror) from the roomy marketing scale to the compact developer-app register
(linear.app/vercel): base 16→14px, nav 13px, labels 11px, display sizes tightened.
Monochrome palette + white-label registry unchanged. A brand/tenant still overrides
any --font-size-* on :root to retune density on demand. 19/19 tests pass.
The publish workflow ran `npm publish` with NO build, but dist/ is gitignored —
so the checkout had no dist/ and the tarball shipped an EMPTY dist/. Published
1.4.0 resolved as a version but every subpath 404'd (`Module not found:
@hanzo/brand/registry`), breaking every consumer's build (console embed).
Add `npm ci && npm run build` before publish (tsup builds index/loader/hero/cli
/brand-id/registry/marks). Bump to 1.4.1 so the working build supersedes the
empty 1.4.0 (caret ^1.4.0 consumers resolve up to it). --no-provenance to avoid
a 422 on the token publish.
Co-authored-by: hanzo-dev <[email protected]>