2656 Commits
Author SHA1 Message Date
zeekayandHanzo Dev 21ea3eae71 ci: retire deploy.yml — one build path
Hanzo CI/CD / cicd (push) Canceled after 0s
CI/CD / cicd (push) Canceled after 0s
The canonical lane is green on this exact commit and publishes a strict
superset of what deploy.yml published, so the second lane is now duplicate
work racing itself to the same registry on every push.

Measured on 6d363b98, both lanes green side by side:

  cicd.yml  (run 27753)  ghcr.io/hanzoai/base:1.5.19
                         ghcr.io/hanzoai/base:latest
                         ghcr.io/hanzoai/base:sha-6d363b9-amd64
  deploy.yml (run 27754) ghcr.io/hanzoai/base:sha-6d363b9

deploy.yml could only ever publish `sha-<short7>` on a branch, which is why
this image had no deployable tag between hand-cut releases; imgver derives the
next patch against the registry (declared=none published=1.5.18 -> 1.5.19), so
every build now earns a version. The `-amd64` suffix is the shape hanzoai/ci
keeps for single-arch precisely because that is what deploys already expect,
and no consumer pins a bare sha- tag: the four in-fleet pins are base:1.3.0,
base:0.39.16, base:cal-a2b6ce3 and base:1.3.2 via SUPERBASE_BASE_IMAGE_TAG.

Everything else deploy.yml did, the reusable already did beside it — buildx,
the GHCR login, the gh_token build secret, and the post-push "does the manifest
actually resolve" check. What it never did was gate anything, and that is the
half this repo was missing: 64 Go packages whose tests CI ran zero of.

Unchanged by this, and stated so nobody reads a regression into it: neither
lane reaches oci.hanzo.ai from this runner (the mirror step warns and skips on
an unreadable registry credential, ghcr push unaffected), and neither deploys.
base does not run anywhere as itself — choosing which tag a consumer embeds
stays a reviewed change in hanzoai/universe.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 18:02:26 -07:00
zeekayandHanzo Dev 6d363b989c store: inject the upload failure with ENOTDIR, not a chmod root ignores
deploy / build (push) Successful in 1m42s
Hanzo CI/CD / cicd (push) Successful in 7m30s
CI/CD / cicd (push) Successful in 7m30s
The three P7-H1 tests poisoned the bucket with `chmod 0500` on the directory
fileblob writes into. CI runs as root (the runner's GOPATH is /root/go), and
root bypasses the DAC write check — so the upload SUCCEEDED, Evict returned
nil, and all three failed asserting on an error that never came:

    multitenant_test.go:632: Evict: err=<nil>, want ErrUploadFailed
    multitenant_test.go:667: Close: expected aggregated upload-failure error, got nil
    multitenant_test.go:745: first Evict should fail: <nil>

They passed for everyone who ran them as themselves, which is why this landed.
It only became visible now: git.hanzo.ai resolved every `type: boolean`
workflow_call input to false, so hanzoai/ci's test step had never executed for
any caller in the fleet. With that fixed, this repo's gates ran for the first
time and said this.

Poison with a regular FILE where fileblob needs a directory instead — its
os.MkdirAll(filepath.Dir(path)) then returns ENOTDIR, which the kernel enforces
against root too. Same injected failure at every uid, and clearing it is one
os.Remove, so the retry test says what it means.

Verified both ways, as root and as uid 1000: go build ./... and go vet ./...
clean, ./store/... 4/4 ok. Nothing in store/multitenant.go changed — the
implementation was always right, the test could not see it.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 17:53:42 -07:00
zeekayandHanzo Dev f9ec706705 build: remove stray .claude worktree gitlink — no .gitmodules entry, breaks every BuildKit clone
deploy / build (push) Successful in 2m24s
Hanzo CI/CD / cicd (push) Failing after 6m45s
CI/CD / cicd (push) Failing after 6m45s
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 17:22:44 -07:00
zeekayandHanzo Dev 259a2030ec ci: the canonical lane — root hanzo.yml + .hanzo/workflows/cicd.yml
deploy / build (push) Successful in 1m54s
Hanzo CI/CD / cicd (push) Successful in 1m54s
CI/CD / cicd (push) Successful in 1m55s
This repo built images and gated NOTHING. 64 Go packages with tests, and CI ran
none of them, which is why `go vet ./...` had been failing on main unseen.

Two files. `hanzo.yml` says what this repo is (three gates, one image, no
deploy); `.hanzo/workflows/cicd.yml` is ~10 lines importing
hanzoai/ci/.hanzo/workflows/build.yml@v1. `.hanzo`, not `.github`: github.com has
zero self-hosted runners for this org, so a job asking for
hanzo-build-linux-amd64 there is never claimed — it waits out the 24h timeout
rather than failing. Gitea also reads only the FIRST of WORKFLOW_DIRS present in
the commit, so `.github/workflows` has been dark here all along.

deploy.yml is deliberately LEFT IN PLACE by this commit. It is the current green
lane (36 runs, main and v* tags alike) and it does not come out until the new one
has been watched green and shown to publish the same image.

The gates, and why each is the command it is:

  go build ./...   fails first and legibly on a compile error
  go vet ./...     was RED on main. One line: an unexported field in
                   tools/router/unmarshal_request_data_test.go carried a `json`
                   tag that the code under test never reads —
                   UnmarshalRequestData defaults structTagKey to "form", and the
                   `form` tag beside it is untouched. So the assertion is
                   identical and vet is honest again. Fixed here rather than
                   suppressed, because a suppressed analyzer is a gate that
                   cannot say what it is for.
  go test ./...    the whole tree; 64 packages ok.

All three export CGO_ENABLED=0, which is not a preference — it is how Base
ships, and testing it any other way tests a program we do not publish. With cgo
ON the runner links a system SQLite with no math functions, `tests.NewTestApp()`
returns an error instead of an app, and every suite that needs one panics on the
nil: measured, 24 of 64 packages FAIL, all one cause. The engine says so itself
("Rebuild with CGO_ENABLED=0 (how Base ships)"). GOWORK=off for the same class of
reason — a parent go.work must not decide which modules CI resolves.

No `deploy:`, transcribing what deploy.yml already said. Base does not run
anywhere as itself: ghcr.io/hanzoai/base is a base image four products embed at
four different pinned tags (platform 1.3.0, hanzo-app-base 0.39.16, cal
cal-a2b6ce3, superbase 1.3.2), each a reviewed change in hanzoai/universe. Even
base.hanzo.ai is served by superbase, which embeds base/ui-react at its own
go.mod pin. Four consumers on four tags is exactly why choosing which one runs is
not a CI side effect, and cd.hanzo.ai's selfHeal would revert a direct patch
within minutes regardless.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 17:08:13 -07:00
zeekayandHanzo Dev 4f12bb1ccc jsvm types: regenerate — committed conflict markers, and 9 namespaces it never had
`plugins/jsvm/internal/types/generated/types.d.ts` carried literal
`<<<<<<< HEAD` / `=======` / `>>>>>>> upstream/master` at lines 23807-23850,
committed, from an old upstream merge nobody finished. The file's own first
line reads GENERATED CODE - DO NOT MODIFY BY HAND, so it was not hand-edited
around: `make jstypes` (go run ./plugins/jsvm/internal/types/types.go) was run,
which is the one way this file is produced.

Regenerating showed the markers were the smaller half of the problem — the file
was badly stale. Nothing was lost (every namespace present before is still
present, `declare function` count unchanged at 17) and NINE namespaces appear
that Base's own packages have exported for some time: crdt, tasks, network,
metric, logger, query, json, big, rsa. Hook authors writing JS against Base had
no types for any of them.

Worth knowing for whoever adds a regenerate-and-diff gate: the generator
prepends `// <unix timestamp>` to line 1, so this file is never byte-reproducible
across runs and such a gate must ignore that line.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 17:08:13 -07:00
zeekayandHanzo Dev 0b35c78378 admin: build the routes before typechecking them
deploy / build (push) Successful in 2m0s
src/routeTree.gen.ts is gitignored and the TanStack vite plugin is the only
thing that writes it — there is no generator binary in this dependency set.
The script ran tsc first, so on a clean checkout it typechecked against a file
that did not exist yet: 'Cannot find module ./routeTree.gen', and then every
route object fell back to undefined for 21 more errors. pnpm build could not
pass on a fresh clone, only on one where a previous vite run had left the file
behind.

vite build writes the route tree and bundles; tsc --noEmit then checks the
result and still fails the script if the types are wrong.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.5.16
2026-08-04 16:53:44 -07:00
hanzo-dev 66c204de86 merge: origin/main into converge/hanzo-8x-ui
The admin UI moved onto @hanzo/design; main moved 19 commits underneath it.
Only ui-react/tsconfig.json overlapped, and both sides had already dropped the
same baseUrl line, so it resolved to itself.

# Conflicts:
#	go.mod
#	go.sum
#	plugins/platform/iam_proxy.go

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 16:43:53 -07:00
hanzo-dev 50c77047e5 admin: a control's edge is --border-control, not --border-strong
@hanzo/design 0.4.2 moved control edges off the neutral ladder and back
onto alpha, so a resting field is a hairline again instead of a mid-grey
box. This sheet was still asking for --border-strong on three controls —
the input/select/textarea, the outline button and the dashed add button —
which is the decoration rung, one step louder than the control rung.

.preview is the exception and stays off the tokens entirely. It paints
its own #fff and this admin is dark-only, and a border composites over
its own element's background, so every white-alpha boundary in the dark
theme draws nothing at all there. It takes rgb(0 0 0 / .16) — the value
--border-strong itself carries in the light theme, because a light ground
needs a light ground's edge.

Pins ^0.4.5, not ^0.4.2: 0.4.2 shipped a stray */ in styles.css that
PostCSS rejects outright, so it is not a version anything can build
against. dist/ is rebuilt here because embed.go serves it.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 11:10:46 -07:00
hanzo-dev aa1c4e351d zip v1.18.1 -> v1.24.2: the JS runtime moved to zip/js
deploy / build (push) Successful in 1m33s
Base's zip surface is two files, and none of the composition changes in
this range reach it — no Use, Add, Graft, Prepare or Router impl, and the
one root-package user is a test calling zip.New/Get/Ctx, all unchanged.

What did break is a package that moved rather than an API that changed:

    github.com/zap-proto/zip/runtime  ->  github.com/zap-proto/zip/js

  go: module github.com/zap-proto/zip@latest found (v1.24.2), but does
      not contain package github.com/zap-proto/zip/runtime

The symbols gojavm uses came across untouched — JSRuntime, JSOptions
(PoolSize), NewJSRuntime keep their shapes — so this is an import path and
an alias, zipruntime -> zipjs, plus the prose that named the old path.

  go build ./...            exit 0
  go test ./...             64 packages ok, 0 failures
  plugins/gojavm            7/7, exercising the renamed runtime for real

go vet reports tools/router/unmarshal_request_data_test.go:454
(unexportedWithTag has a json tag but is not exported). That is the
fixture doing its job and it reports identically at v1.5.14; this change
does not touch the file.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.5.15
2026-08-04 09:33:35 -07:00
hanzo-dev 0d42fd3522 Ask the SQLite engine for its math functions instead of asking the build
deploy / build (push) Successful in 1m59s
core/sqlite_math_required.go failed any cgo build not carrying the
sqlite_math_functions tag, by referencing a symbol that was never defined
anywhere. So `go build ./...` — no tags, default CGO_ENABLED — did not
work in this repo or in any repo importing core, and the error named a
symbol rather than the problem:

    undefined: cgoBuildNeedsSQLiteMathFunctions

The capability it was protecting is real. tools/search emits acos, cos,
sin and radians for geoDistance, and SQLite only has those when compiled
with SQLITE_ENABLE_MATH_FUNCTIONS. Measured, per config:

    CGO_ENABLED=0                              present   (what the Dockerfile ships)
    CGO_ENABLED=1                              ABSENT    <- the one deficient config
    CGO_ENABLED=1 -tags sqlite_math_functions  present
    CGO_ENABLED=1 -tags libsqlite3             present   <- blocked anyway

The tag was a proxy for the capability, and the proxy was wrong. Row four
is the config the driver's own doc comment calls production: that tag
links the system sqlcipher, so the amalgamation's -D flag is never
compiled and the tag decides nothing, yet the guard rejected the build.
It also could not have caught a system libsqlcipher genuinely missing the
functions, and no library can satisfy it from the inside, because build
tags are chosen by the top-level build command.

Replace it with a measurement. VerifySQLiteMathFunctions runs the
geoDistance expression once per process against a throwaway in-memory
database and DefaultDBConnect refuses to hand back a connection when the
answer is no, so a deficient binary dies at startup with the three ways
to fix it rather than at a customer's failed search. A probe that could
not run at all is not an error: this reports a measured absence, never a
failure to measure.

Detection is strictly better than before. tools/search already had
TestTokenFunctionsGeoDistanceExec executing the real emitted SQL, and it
fails on the deficient config today; core now carries its own, since
tools/search does not import core and its failure said nothing about
this package.

  build, all four configs above          exit 0 (was: 2 of 4 refused)
  go test ./... CGO_ENABLED=0            64 packages ok, 0 failures
  go test ./core -run SQLiteMath CGO=1   FAIL, as it should

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.5.14
2026-08-04 09:27:45 -07:00
hanzo-dev b9dd270a89 admin: vite 8, and drop the two deps that were holding it back
Every other package in this admin was already on its latest published version;
vite was two majors behind at 6.4.3. Both plugins support 8 at their latest
(@vitejs/plugin-react 6.0.5 peers vite ^8.0.0, router-plugin 1.168.23 peers
>=8.0.0), so the pin was the only thing keeping it there.

`esbuild` was a direct devDependency that nothing imported: no source file
references it and no plugin here peers on it — router-plugin's peer list is
rsbuild/vite/solid/webpack/react-router. It only surfaced as a problem because
vite 8 peers esbuild ^0.27||^0.28 and 0.25 tripped it, which is how a
vestigial dep announces itself. Removing it is what silenced the warning;
bumping it would have kept an unused package on the list.

`build.rollupOptions.output.manualChunks: undefined` set a default to its own
default. Removing it produces byte-identical output (same content hashes), and
under rolldown the option is spelled elsewhere anyway, so it was config that
could only mislead.

Rolldown does the same work in a fraction of the time and ships less of it:
7.9s -> 1.3s, 871 kB -> 836 kB (gzip 269 -> 254).

Verified the bundle, not just the build — the whole point of smoke.mjs is that
a green build says nothing about what renders, and a bundler swap is exactly
the change that could quietly break it. All 19 routes paint, every token
resolves, no utility-class residue, no unprobed control, overlays open, confirm
dialog measures 384.

pnpm build 0 (vite v8.2.0, built in 1.34s) · pnpm smoke SMOKE OK 0 ·
CGO_ENABLED=0 go build 0 · go test -count=1 ./ui-react/... ./apis/... ok 144.5s

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 01:05:08 -07:00
hanzo-dev aeda88d20a admin: assert the dialog width the smoke test only printed
The 8.x rewrite this finishes traded ~220 utility classes for a token sheet
and moved the overlays onto @hanzo/ui + @hanzo/gui. gui drops a prop it does
not recognise in SILENCE, so the smoke test opened the confirm dialog and
measured it — but measured it mid enter-animation, read ~viewport width, and
then never compared the number to anything. It printed 1265 next to a
`maxW={384}` and still said SMOKE OK.

Settle a frame, read the panel, and gate on it. The expected width is parsed
out of the route that asks for it, so the check cannot drift from its subject.
Proved the gate fires by patching `maxW:384` to `maxW:void 0` in the built
bundle: 1239 vs 384, SMOKE FAILED, exit 1.

Same class of hole one layer up: a control probed as null was reported as
"absent from the output" rather than as a check that never ran. `.panel` was
absent on every route because the run signs in before its first navigation and
/login is the only page that renders one. Look at the signed-out login card
first, and fail on any control that was never on screen.

Also retires what the rewrite left behind:

- `authWithPassword` in api.ts and base-client.ts. The fork retired local
  password auth and the server 404s `auth-with-password` for EVERY collection
  (apis/middlewares_test.go); login is IAM PKCE. Dead code aimed at a dead
  endpoint.
- `scripts/sync-admin-ui.sh`, which copied a bundle built out-of-tree in
  gui/apps/admin-base. The SPA is built here now, so that was a second way to
  do one thing.
- `ui-react/.smoke/`, scratch probes superseded by smoke.mjs (they hardcode an
  absolute path to another checkout and carry a stale built bundle).

LLM.md and ui-react/README.md described the stack this replaced — pin
@hanzo/ui@^5.7.1, adopt vendored shadcn, wire Tailwind v3, sync from
gui/apps/admin-base — under the old package names. Rewritten to what is true:
@hanzo/ui on @hanzo/gui, zero Tailwind/Radix/shadcn, tokens as plain CSS
custom properties, and the reason a green build proves nothing here.

pnpm build 0 · pnpm smoke SMOKE OK 0 · go build 0 · go vet 0 · go test
./ui-react/... ./apis/... ok 70.5s (CGO_ENABLED=0, the way it ships —
core/sqlite_math_required.go is a deliberate compile error for cgo builds
without -tags sqlite_math_functions).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 00:58:25 -07:00
hanzo-dev f36488d6c2 wip: preserve agent work interrupted by session limit
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-04 00:30:46 -07:00
zeekay f9e69ff018 rpc: /ext/ -> /v1/ on our own hosts
deploy / build (push) Successful in 1m29s
luxd serves /v1/*; /ext/* 404s. Third-party chains keep their own paths.
2026-08-03 21:19:47 -07:00
hanzo-dev 79d6d1d0cb wip: preserve agent work interrupted by session limit
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-03 19:18:14 -07:00
hanzo-dev 4a0eda9c09 deps: mark cek and namespace direct, which is what they now are
deploy / build (push) Successful in 5m7s
go mod tidy. The previous commit imports both from platform and vault,
so neither is indirect any more, and go-jose drops out with the tasks
bump that came with it.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.5.13
2026-08-02 19:59:39 -07:00
hanzo-dev 1e51224efd keys: derive every key with cek, and delete the ones that did not
deploy / build (push) Canceled after 3m51s
base had its own key derivations and no dependency on hanzoai/cek, the
primitive that is meant to be the only one. Two HKDFs in the platform
plugin, two HMACs beside them for S3, and a two-level HMAC hierarchy in
the vault. None agreed with cek, and the comments describing them had
drifted off the code: org_db and platform.go both said HMAC where the
code ran HKDF, and the vault drew a KEK hierarchy in its package doc.

All of them are now cek.DeriveKey(master, namespace, subsystem):

	org database   org/{org}          "org"
	user database  org/{org}/{user}   "user"
	org objects    org/{org}          "s3"
	user objects   org/{org}/{user}   "s3"
	vault shard    org/{org}/{user}   "vault"
	shared vault   org/{org}/{vault}  "vault-shared"

A per-org and a per-user key are one derivation over two namespaces,
which is cek's argument: the key is bound to the name that renders the
path. namespace.Key refuses a user namespace, but that is about LAYOUT
and DeriveKey does not use it — a person's data is named org/{org}/{user}
here, which is what namespace.Of already builds, and it is what keeps
acme/alice and globex/alice on different keys. The vault reaches the same
namespace through OrgProject, because its ids are DIDs and a colon is not
legal in a segment; Sanitize is injective, so two DIDs cannot fold onto
one vault.

The vault's org KEK is gone. It was there to be rotatable without
re-encrypting user data, nothing ever rotated it, and the org is in the
namespace now — the property it was actually providing.

store/keyring.go keeps its own HKDF, and now says why. It WRAPS a random
age identity rather than deriving a key, so Rotate re-wraps one sidecar
instead of rewriting every byte of ciphertext, and its secret is a
per-org KMS KEK rather than one master. cek has no wrap by design. A
different shape, left alone rather than bent into one.

Tests now compare each derivation against cek.DeriveKey directly. A
comment claiming a derivation cannot fail when the derivation drifts,
which is exactly how the one deleted last time survived.

A master key of the wrong length is now an error instead of silently no
key, and the resolver's encKey, computed and never read, is gone.

deps: cek v0.2.3 and namespace v1.2.0, plus tasks v1.52.4 -> v1.52.9,
which is what moves hanzoai/sqlite to v0.5.0 — tasks below v1.52.9 still
calls the wrapped-DEK API that v0.5.0 removed.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.5.12
2026-08-02 19:56:10 -07:00
Hanzo Dev b052b5cb8a Delete a key derivation that claimed a compatibility it did not have
deploy / build (push) Successful in 1m50s
core/encryption.go said "Compatible with hanzoai/sqlite's CEK derivation".
It was not, and had not been: base derives with salt
"base.cek.v1.hkdf-sha256-salt..." and info "{type}:{id}", while the sqlite
version base pins derives with a nil salt and length-prefixed info. The same
master key and principal produce different 32 bytes in the two, so anything
built on the claim would have written files the other could not open.

Nothing called it. Not base, not any repo in the workspace — the derivation
functions and their PrincipalType were dead, and what survived was the false
sentence in the comment, which is worse than the code.

Key derivation belongs to hanzoai/cek, which binds the key to the same
namespace that renders the file's path, so a key and a location cannot
disagree about which account they belong to.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-01 22:04:49 -07:00
Hanzo Dev 1a5ff5c50e Strip the headers that say who pays and what they may do
deploy / build (push) Successful in 1m43s
This list exists so a service can drop client-supplied identity headers before
its own auth re-injects them. Five were missing from it, and they are the five
that matter most:

    X-User-Permissions     an authorization bypass
    X-User-IsGlobalAdmin   an authorization bypass
    X-Billing-Account-Id   the account the request is charged to
    X-User-Owner           the home org
    X-Project-Id           the sub-scope

The gateway strips all five. This copy did not, so any service sitting behind
this middleware and NOT behind the gateway trusted whatever the client sent —
and that is exactly the case this middleware exists for.

The two lists live in modules that do not import each other, which is why they
drifted. A test now pins every name the gateway strips; it fails with
"X-Billing-Account-Id survived as forged-by-the-client" if one goes missing
again. One list in one place is the real fix and is a separate change.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-01 21:55:40 -07:00
Hanzo Dev 52273b3921 Strip the headers that say who pays and what they may do
This list exists so a service can drop client-supplied identity headers before
its own auth re-injects them. Five were missing from it, and they are the five
that matter most:

    X-User-Permissions     an authorization bypass
    X-User-IsGlobalAdmin   an authorization bypass
    X-Billing-Account-Id   the account the request is charged to
    X-User-Owner           the home org
    X-Project-Id           the sub-scope

The gateway strips all five. This copy did not, so any service sitting behind
this middleware and NOT behind the gateway trusted whatever the client sent —
and that is exactly the case this middleware exists for.

The two lists live in modules that do not import each other, which is why they
drifted. A test now pins every name the gateway strips; it fails with
"X-Billing-Account-Id survived as forged-by-the-client" if one goes missing
again. One list in one place is the real fix and is a separate change.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-08-01 21:55:24 -07:00
Hanzo Dev 16bcae38cd deploy: a v* tag is a release and must publish an image named after it
deploy / build (push) Successful in 3m40s
The builder answered only to `push: branches: [main]` and unconditionally
tagged its output `sha-<7>`, so it could not publish a semver image at all
and `git tag` published nothing. ghcr.io/hanzoai/base could therefore carry no
version, which is why the estate's manifests pin it by sha or by a bare
major.

Same defect and same fix as hanzoai/iam, where it had left SIX consecutive
release tags (v1.33.32..v1.33.37) with no image while production ran an
unreleased commit.

  on.push.tags: ['v*']   a tag push now builds.
  meta                   a tag push publishes the TAG; a branch push still
                         publishes an immutable sha-<7>.

Both stay traceable and only the first is deployable -- the estate guards
require a semver tag (or a digest) in any manifest. A non-`v` tag falls
through to sha-, so an odd tag name cannot publish a bogus version.

Owner directive: everything we publish carries real semver, no ad-hoc tags.

Co-Authored-By: Hanzo Dev <dev@hanzo.ai>
2026-08-01 13:03:32 -07:00
hanzo-dev 35efd81d76 wip: preserve in-flight work
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-29 16:29:15 -07:00
hanzo-devandHanzo Dev 2e6600fd4b tasks: the embedded engine listens on a socket, not a port
deploy / build (push) Successful in 1m30s
tasks v1.52.4 replaced EmbedConfig.ZAPPort with Address, where a filesystem
path binds a unix socket. Nothing off this host dials the embedded engine, so
there is no port to allocate and none to collide over — two Base processes on
one machine used to fight over 9999 and the loser came up with no durable
engine at all, silently.

The default socket lives under the app's own DataDir rather than a shared temp
dir, so the collision cannot reappear one layer down: the path names whose it
is. TASKS_EMBED_ADDRESS overrides it and takes a host:port just as well, for a
deployment that does want a remote listener.

client.Options.HostPort became Address on the same release; the two dial sites
and the embed test move with it, and the test now runs over a socket.

luxfi/vm moves v1.3.1 -> v1.3.2, which is what cloud already runs. The pinned
v1.3.1 hash was not the one the checksum database holds for that tag, and its
go.mod hash was v1.3.2's — a moved tag, so the module could not be verified at
all. v1.3.2 verifies.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.5.11
2026-07-28 23:03:48 -07:00
hanzo-dev 45ad3f1da6 kms: reach the live KMS over ZAP, and stop importing an archived repo
deploy / build (push) Failing after 20s
hanzoai/kms is archived: `github.com/hanzoai/kms/sdk/go v1.1.1` can never
be patched again, and base had it in production code. The repo's own
DEPRECATED.md already names the successor — `github.com/hanzoai/kms` (Go
import) -> `github.com/luxfi/kms` — which is live, and is the engine the
live service (cloud/apps/kms) embeds. So this is not a repo move; it is
finishing a migration that was declared and never completed.

Two things were frozen in that SDK, and neither survives contact with the
live service:

1. THE ORG WAS MISSING FROM THE ZAP COORDINATE. The KMS store shards its
   tenant boundary on the "orgs/{org}" segment of the secret PATH; a path
   without it is deployment-wide. The SDK carried the org in the HTTP URL
   only — on ZAP it passed the caller's bare path straight through and set
   the org as a DefaultPath the calls never used. ZAP is the transport
   operators are told to use in-cluster, so every org read and wrote the
   SAME deployment-wide record. ref() now folds the org into the path, and
   TestKMSSecretsAreTenantScoped fails if it ever stops.

2. THE HTTP ROUTES NO LONGER EXIST. The SDK speaks
   /v1/kms/orgs/{org}/secrets/...; the live service serves /v1/kms/secrets
   and takes the tenant from the validated principal. Rather than re-point
   a second transport at a second shape, the HTTP path is gone: in-cluster
   Go speaks native ZAP to KMS, one way. An http(s) endpoint is now
   rejected at Register, where an operator sees it, instead of degrading
   every secret read into the env fallback.

plugins/kms is deleted. It was a client for /v1/orgs/{org}/zk/* — a wire
surface no server in the fleet implements — nothing registered it, and
cloud/apps/mpc already carries the only live implementation of that
protocol. It was also the only remaining user of the archived SDK's root
package.

Net: base drops a module it could not patch, gains a direct dep on one it
already carried transitively, and loses 1,723 lines.

go.sum: luxfi/vm v1.3.1 reconciled to the hashes the proxy now serves —
main did not build without it (the tag moved upstream).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.5.10
2026-07-28 22:49:56 -07:00
zooqueenandHanzo Dev 2f273e2a07 docs: say IAM, not the vendor name it was never derived from
deploy / build (push) Successful in 1m35s
Hanzo IAM (hanzoai/iam) is original clean-room work — its LICENSE says so.
The third-party-derived identity server was hanzoai/iam-v1, and that is
retired. Comments here named the old vendor as if it were the IAM we ship,
which was simply false.

Every hit was a comment or doc. The behavior each one describes is real and
still described; only the name changed:

- envelope shape {status, msg, data} and status:"error"-at-HTTP-200 are
  attributed to IAM, which is what the fakes actually emulate
- the phone-normalization probe still explains why three shapes are tried
- the /v1 vs /api/* mount notes now state the rule directly: /v1 is Base's
  one external prefix. Outbound /api/* calls to the IAM server are its
  contract and are untouched.
- hanzo.id not accepting HTTP Basic is stated on its own

No identifiers, routes, env vars or wire fields changed.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-28 17:12:32 -07:00
zeekayandClaude Opus 5 05ce94fe34 deps: exclude versions that no longer exist upstream
deploy / build (push) Successful in 1m25s
The module graph could not be loaded at all: transitive requirements name
versions whose tags were wiped or whose pseudo-versions point at commits that
history rewrites removed, so `go list -m all` fails with "unknown revision" or
"invalid pseudo-version". Builds still passed because those modules supply no
package that gets compiled — the breakage is graph-level, which is exactly the
kind that only shows up on a clean checkout.

`exclude` is the mechanism for "this version is unusable": MVS then selects the
next higher one instead of trying to fetch a revision that is gone. Bumping the
requirement alone does not work, because Go still reads the excluded version
go.mod while building the graph.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 16:09:31 -07:00
z 09420345d7 build: fetch public luxfi from the proxy, not direct git
deploy / build (push) Successful in 1m24s
The image build fails at `go mod download`:

  verifying github.com/luxfi/vm@v1.3.1/go.mod: checksum mismatch
    downloaded: h1:uViH3COP8hh…   (direct git)
    go.sum:     h1:6YR/uFV2Fo…
  SECURITY ERROR

The comment above these lines claimed go.sum "is tidied against git", so the
proxy would be the thing that mismatches. It is the other way round. Checked
entry by entry against sum.golang.org: ZERO of this go.sum's luxfi lines diverge
from the signed database — including the vm@v1.3.1 line above, which matches it
byte for byte. go.sum is sumdb-consistent throughout, so direct git is the
source that disagrees, on exactly the tags luxfi force-moves.

Preferring the proxy is also the safer default: it serves the immutable copy
sum.golang.org signed, so re-cutting a tag can no longer change what this image
is built from without the checksum saying so.

All 40 luxfi modules in this graph were verified present on proxy.golang.org
before making the change. hanzoai/* (dbx, kms/sdk/go, ltx, pubsub-go, tasks,
tygoja) are private, cannot be proxied, and stay direct behind the gh_token.

Evidence this is the right fix: the identical change on hanzoai/superbase took
its image build from failing to green (superbase e47d247, run #4 success).

Caveat worth recording — I could NOT reproduce this locally in either mode, and
the reason is instructive: ~/work/hanzo/go.work puts 8 modules in a workspace,
so a local `go mod download` resolves a different graph than the Dockerfile
does. With GOWORK=off both proxy and direct verify cleanly here. If this build
still fails after the change, suspect the BuildKit `--mount=type=cache,
target=/go/pkg/mod` holding re-cut bits from an earlier build, and bust it.
2026-07-28 03:51:02 -07:00
zeekayandClaude Opus 5 cc87610ee4 deps: drop go.sum lines that disagree with the checksum log
deploy / build (push) Failing after 27s
These entries were recorded from direct VCS fetches made while GOPRIVATE
covered github.com/luxfi/*, which switches off both the proxy and checksum
verification. The tags had since moved, so the recorded hashes disagree with
what sum.golang.org holds and the build refuses to verify.

proxy.golang.org still serves the originally-published bytes, so dropping only
the disagreeing lines and re-tidying restores a verified build with no version
change. Lines that agree were left untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 01:30:13 -07:00
zandHanzo Dev d64d0cf098 fix(docker): golang 1.26.4 -> 1.26.5, the version go.mod already requires
deploy / build (push) Successful in 2m11s
go.mod declares `go 1.26.5` while the builder pinned golang:1.26.4. The golang
images set GOTOOLCHAIN=local, so the base can never fetch a newer toolchain to
satisfy the directive and the build dies immediately:

    go: go.mod requires go >= 1.26.5 (running go 1.26.4; GOTOOLCHAIN=local)

A release build that fails in under a second did not fail to compile — it
failed to acquire a toolchain.

Found by sweeping every hanzoai repo with a go.mod against its Dockerfile base:
25 declare go >= 1.26.5, and 16 pin an explicit 1.26.4 that cannot satisfy it.
Repos on a floating `1.26` / `1.26-alpine` are already fine — both now resolve
to 1.26.5 (verified by digest: golang:1.26-alpine and golang:1.26.5-alpine are
the same sha256:0178a641).

Patch bump only, image variant preserved, and the target tag verified to exist
before the change.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-27 19:10:15 -07:00
Hanzo Dev 35ed2f51a7 deps: zap-proto/zip v1.17.2 (+ zap-proto/http v0.3.1)
deploy / build (push) Failing after 3m54s
v1.17.2 is the fleet standard for the ZAP transport, and ZAP peers only
interoperate when they agree on the zap-proto/http wire version. This one is
not a wire break: base already crossed the v0.2 -> v0.3 framing change back at
zip v1.10.0, and v0.3.1 keeps the length-prefixed name/value headers, only
making the decode path allocation-free. A v0.3.0 peer and a v0.3.1 peer still
talk, so this can land without moving in lockstep.

The go directive moves 1.26.4 -> 1.26.5 because zip v1.17.x declares that
floor; `go get` raised it. It is a consequence of the bump, not a separate
decision.

No call sites changed. zip's breaking edits across this range are the removal
of AdaptNetHTTPFunc and the currying of Load(prefix, p) into
Load(p, prefixes...). base uses neither — its whole zip surface is
zip.New/Config/Ctx in conn_memory_test.go and zip/runtime in plugins/gojavm,
none of which moved.

CGO_ENABLED=0 go build ./... green (shipping mode), green again with -tags
sqlite_math_functions (the cgo mode core/sqlite_math_required.go gates).
go test ./... passes, 65 packages.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.5.9
2026-07-27 18:05:10 -07:00
Hanzo Dev f38eabfea6 fix(license): correct fork point in NOTICE
NOTICE claimed the fork was taken at pocketbase v1.5.5. That tag does not
exist upstream (max is v0.39.9); v1.5.5 is Base's own tag. The actual fork
point is upstream v0.39.0 = aeb78e51a1
(2026-05-28), verified by matching the last upstream-authored commit.

An inaccurate fork point undermines the attribution it is meant to record.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-27 16:23:46 -07:00
Hanzo Dev 1beb0e0bdf network: remove the orphans the GCS archive rip left behind
deploy / build (push) Successful in 5m3s
Second pass on e7d6bb42. That commit made s3:// the only archive scheme
but left the surface that only existed to serve gs://:

- network/archive_gcs_integration_test.go: tested newGCSArchive, deleted
  with the backend. NewArchive now dispatches `case "s3"` and returns
  "unsupported scheme" for everything else (archive.go), so the test could
  only ever hit its own t.Fatalf. It was gated behind `integration && gcs`
  and `gcs` is now declared by no file in the tree and passed by no
  Makefile, Dockerfile or workflow — nothing could have run it.
- archive_integration_test.go pointed readers at that file as "a GCS
  variant lives alongside".
- archive_writer.go described `uploader` as the seam "S3 and GCS
  implement" and archiveWriter as what "both the S3 and GCS backends sit
  behind". There is one backend.
- config.go advertised gs://bucket/prefix as an accepted Archive value —
  a value that now returns an error.
- README architecture diagram claimed replicate ships to S3/GCS; replicate
  dropped its gs backend too.

Kept: cloud.google.com/go/storage and /iam in go.mod. They look orphaned
but `go mod why` traces them through hanzoai/replicate v0.9.5, which still
contains gs/replica_client.go — the require is real until base bumps
replicate past its own gs/abs/oss rip. Removing it would break the build,
not tidy it.

CGO_ENABLED=0 go build ./... green (shipping mode); go vet ./network/...
clean both plain and with -tags integration.

Co-Authored-By: Hanzo Dev <dev@hanzo.ai>
2026-07-27 14:47:17 -07:00
zeekayandHanzo Dev ab9240a7b5 build: migrate tsconfig to TypeScript 7 (native compiler)
deploy / build (push) Successful in 2m36s
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 <dev@hanzo.ai>
2026-07-27 14:18:11 -07:00
zeekayandHanzo Dev b4c279ca43 style: gofmt the apis package
deploy / build (push) Successful in 5m5s
Ten files under apis/ were left unformatted; this is `gofmt` output only
— re-indentation, struct tag alignment, and one import re-sorted per file.
No semantic change (`git diff -w` reduces to the moved import lines).

Co-Authored-By: Hanzo Dev <dev@hanzo.ai>
2026-07-27 13:25:07 -07:00
Hanzo Dev 570938663a test: align the IAM mock with the path its client actually sends
The fake IAM answered /api/get-users while LookupByAttribute has sent
/v1/iam/get-users for some time, so the case was unreachable. The test still
passed — the caller treats a lookup error as 'no match' — which is why the
drift went unnoticed. Instrumenting the branch confirms it is still not
exercised by any test here; that gap is pre-existing and worth its own test.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-27 11:47:57 -07:00
Hanzo Dev 93eb73b0a9 platform: address IAM at /v1/iam, the prefix it actually serves
auth_proxy still proxied login/signup/userinfo/send-verification-code at a
prefix the edge refuses with 403, while iam.go beside it had already moved.
One dialect now, matching the rest of the package.

The guard reads string literals through go/ast rather than grepping lines, so
a comment recording a dead upstream path stays documentation and only a real
route can fail the build.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-27 11:36:51 -07:00
zeekayandHanzo Dev 51dbad29c4 deps: zap-proto/zip v1.10.0 (+ hanzoai/orm v0.6.16)
deploy / build (push) Successful in 4m7s
zip v1.10.0 picks up zap-proto/http v0.3.0, where headers on the wire are
length-prefixed name/value pairs instead of a JSON map. That is a wire break — a
v1.10.0 process cannot talk to a v1.9.x one — so every ZAP service moves together.

orm moves in the same commit where it is a dependency: v0.6.14 called
zaphttp.NewTransport, which http v0.3.0 removed, so the two versions cannot be
split across a build.

Builds clean.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-27 10:28:55 -07:00
hanzo-dev d7632c3e8f ci: drop the Gitea mirror-sync nudge
deploy / build (push) Canceled after 3m16s
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.
2026-07-27 10:25:39 -07:00
hanzo-dev 6cee335f5e deps: zap-proto/zip v1.2.0 -> v1.9.0, http v0.2.0 -> v0.2.2
deploy / build (push) Successful in 2m0s
Base was already off the dead github.com/hanzoai/zip fork — the migration
landed upstream on main (zap-proto/zip v1.2.0); the two local worktree
branches carrying it (feat/sqlite-hanzo-driver, chore/tasks-v1.50.0) are
fully superseded by origin/main, which also ships newer tasks v1.52.0 /
sqlite v0.3.2 / keys v1.4.1. Nothing to merge forward; this is the
remaining version bump.

v1.9.0 adds Service/Add/Load/Reload/Unload, Mount(prefix,addr), unix-socket
ZAP and Transport{Serve,Dial}. Base's surface is only zip.New/Config/Ctx
(conn_memory_test.go) and zip/runtime's JSRuntime/NewJSRuntime/JSOptions
(plugins/gojavm) — none of it touches the v1.8.4 breaking changes
(zaphttp.NewTransport -> Dial, RegisterTransport's func arg -> Transport
struct), so no call sites moved.

Transitively swaps gofiber/fiber/v3 v3.2.0 for zap-proto/fiber/v3 v3.2.1.

Verified CGO_ENABLED=0 (the way the image ships; core/sqlite_math_required.go
makes a cgo build without -tags sqlite_math_functions a deliberate compile
error): build + test green for core, store, tools/archive, plugins/gojavm and
the root package. TestConnMemory holds 1000 live conns against a real
zip v1.9.0 server at 8.13 KiB/conn, 1.00 goroutine/conn.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-27 09:45:43 -07:00
zeekayandHanzo Dev 79a34d5733 ci: verify corrected pipeline (natural mirror cycle)
deploy / build (push) Successful in 2m51s
Pushed WITHOUT a manual mirror-sync. A manually triggered sync appears to
advance the forge ref without raising a push event, after which the scheduled
sync finds nothing new and no run ever fires — the commit lands but CI never
runs on it.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-27 09:09:20 -07:00
zeekayandHanzo Dev 7b104eae16 ci: exercise the corrected pipeline
deploy / build (push) Canceled after 0s
Empty commit — mirror-sync alone does not reliably raise a push event on the
forge, and workflow_dispatch is accepted (204) while creating no run.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-27 09:02:33 -07:00
z cef4f2e9a7 ci: pass the secret under the id the Dockerfile mounts (gh_token)
deploy / build (push) Canceled after 0s
The build reached buildx and failed inside the RUN that does
  --mount=type=secret,id=gh_token ... cat /run/secrets/gh_token
because the workflow passed it as GIT_AUTH_TOKEN. A mismatched secret id is
not an error at the mount — the file is just empty, so the private-module
fetch fails deep in the layer with no mention of the real cause.
2026-07-27 08:56:26 -07:00
zeekayandHanzo Dev b0e3aa912b ci: trigger native pipeline after ARC decommission
deploy / build (push) Failing after 28s
Empty commit to exercise the rewritten .hanzo/workflows/deploy.yml on
git.hanzo.ai. workflow_dispatch is accepted (HTTP 204) but creates no run on
this forge — every run is ev=push — so a real push is the only reliable
trigger. The previous failures were push events that predated the rewrite
reaching the mirror.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-27 08:47:44 -07:00
Hanzo Dev be6bf7dd62 ci: build an image that can actually be built
deploy / build (push) Canceled after 0s
The previous pipeline called buildctl-daemonless.sh, absent from the job image
(catthehacker/ubuntu:act-24.04 per GIT_RUNNER_LABELS) — exit 127 — and read
secrets.GIT_CLONE_TOKEN, which exists on neither the repo nor the hanzoai org.
It had never produced an image.

Its kubectl step failed the same way and was futile regardless: cd.hanzo.ai
selfHeal restores the App CR from the reviewed pin in hanzoai/universe within
minutes, so the deploy step is removed rather than repaired.

This is now urgent rather than cosmetic: actions-runner-controller was
decommissioned 2026-07-27 and every GitHub workflow pinning an ARC pool was
removed, so this pipeline is the ONLY CI this repo has.

Replaced with the shape proven on hanzoai/playground and 14 siblings:
setup-buildx + login-action + build-push-action using GHCR_USER/GHCR_TOKEN/
GH_PAT, which DO exist as org secrets, plus an imagetools check because
build-push-action can exit 0 before the manifest resolves.
2026-07-27 08:31:18 -07:00
hanzo-dev a373cfc293 ci: nudge git.hanzo.ai to pull on push
deploy / deploy (push) Failing after 5s
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.
2026-07-27 08:09:22 -07:00
Hanzo Dev b542f8bd54 ci: remove GitHub CI — native git.hanzo.ai + cd.hanzo.ai only
deploy / deploy (push) Failing after 5s
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.
2026-07-27 07:56:21 -07:00
Hanzo Dev bda411db62 ci: remove GitHub CI — native git.hanzo.ai + cd.hanzo.ai only
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.
2026-07-27 07:56:20 -07:00
Hanzo Dev fd16853549 ci: remove GitHub CI — native git.hanzo.ai + cd.hanzo.ai only
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.
2026-07-27 07:56:18 -07:00
Hanzo Dev fd1cba7a49 ci: remove GitHub CI — native git.hanzo.ai + cd.hanzo.ai only
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.
2026-07-27 07:56:17 -07:00
zeekayandClaude Opus 5 b6d7b15f09 tests: stop asserting numbers and strings that were never ours to pin
deploy / deploy (push) Failing after 4s
Every failure in this repo came from an expectation that was correct when
written and had no way to stay correct.

Collection counts (apis x6, core x3). `27` was hand-derived in a comment —
"17 framework/system + demo collections plus the 10 CRM ... collections" — and
the delete-event counts were a second hand-derivation, `9 + 10 = 19`. The
calendar-booking migration made them 31 and 23 and both files had been failing
since. Now read from the app: the total from CollectionQuery, and the deletes as
"all non-system, less the one the payload updates". A count of EVERYTHING is
invalidated by every migration that adds anything, so it cannot be a literal.

`SQL logic error` (apis x2). SQLite now says `near "invalid": syntax error`.
That assertion pinned a THIRD-PARTY string; the tests around it already check
status 400, `"data":{}`, our own `Raw error:` prefix, and — for the rollback
case — that the table was not created. Those are ours and they are the point.

IAM mock paths (iam x12). The mock still served `/api/get-users` and
`/api/add-user` after the client moved to `/v1/iam/*`. The client had a stale
doc comment saying `/api/add-user` too. Both fixed; the four tests were failing
with a bare `404 page not found`, which names nothing.

And one that was NOT a stale test. tools/search generates SQL calling acos, cos,
sin, radians and sqrt. The pure-Go backend (CGO_ENABLED=0) has them and that is
what the Dockerfile ships, so production is fine — but the cgo backend only gets
them behind csqlite `sqlite_math_functions`, so a cgo build silently has a
SMALLER SQL surface than the code written against it, and nothing says so until
a geoDistance filter returns "no such function: acos" from an endpoint that
works in prod. core/sqlite_math_required.go makes that combination a build
error naming the fix. Two build modes that answer differently is the bug.

Verified: full suite green as shipped (CGO_ENABLED=0), core twice.
network/ does not import core, so the cgo CI attack suite is unaffected.
TestNotifyWatcher_CollectionsUpdate is a pre-existing timing flake — passed 3/3
alone and on both full re-runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 07:34:22 -07:00