82 Commits
Author SHA1 Message Date
zeekay 51460827d4 Merge the forge's lux/universe into GitHub's, the one main from here
The two mains shared no history, so this is a per-file three-way merge:
for each file the 65 forge-only commits touched (by patch-id), the base is
the newest version both histories held. 105 files merged or took one side
cleanly, 48 were already identical, and the 45 true conflicts and 31
modify/delete pairs were resolved by what runs on the cluster:

- The forge wins where it is live: explorer 1.5.56 (digest 45db11ff) and
  its archive readers, explore-fe-hanzo's ConfigMap env (AI coin,
  /v1/indexer/hanzo, /v1/chain/hanzo/rpc), exchange-api reading
  luxd-archive, and explore-fe-{lux,zoo}-testnet, pinned to what they run
  (v2.8.5-lux / v2.8.4-lux).
- GitHub wins where it is live: explore-fe-lux v2.8.5-lux, explore-fe-zoo
  v2.8.4-lux, bankd v1.0.18, lux-build's routes.
- The forge's retirement of workloads that run nowhere stands: lux-mainnet
  dex, lux-hb2, lux-market, traffic, wallet-web; the lux-testnet frontends,
  indexer, exchange and gateways; lux-operator. deploy/lux-testnet/luxd.yaml
  stays, since roll_luxd.sh reads it.
- GitHub's deletions stand: lux-finance and lux-financial (static sites
  now), the k8s/ operator manifests, .hanzo/workflows/cicd.yml.
- Paths are /v1/chain/C/rpc where the forge had written /v1/chain/c. All
  four spellings answer; this one is the documented form. The forge's
  /{network}/v1 doc lines are dropped: a network is a host, and the prefix
  404s.

Dry-run against the running objects (server-side, as CD applies), the
merged explorer shows no change where GitHub's showed 152 lines, and no
other live workload moves further from what runs. Every deploy/*/*.yaml
renders with charts/app; price.yaml, which failed on GitHub, now renders.

Claude-Session: https://claude.ai/code/session_01TzirYkGmD88w4GipqivUtV
2026-09-23 20:20:03 -07:00
zeekayandHanzo Dev a32988281e lux-bank runs bankd v1.0.18, and Lux ID is the one sign-in the suite proves
bankd v1.0.18 takes only a lux.id access token issued to lux-financial for
the lux org, maps only a member of the admin org to _superusers, and listens
on HTTP alone. Its pod reads none of the bank-secrets keys, so the five env
references to them are gone.

bank-docs states the amd64 node selector its pod already runs with.

e2e/40-lux-financial proves the one way in: neither console serves a password
input or ships a credential in its JS; every sign-in screen hands off to lux.id
with client lux-financial, PKCE S256 and the console's /auth/callback; bankd
answers 404 on every password and Base sign-in route, the code exchange takes
only lux-financial by code or refresh, and a token lux.id did not issue to
lux-financial reads as anonymous. The password-form sign-in and the seeded
ledger totals it asserted are deleted: neither exists.

k8s/lux-k8s/{bank-api,bank-dash,bank-docs,bank-sandbox,bankd,financial} and
scripts/seed-bank-admin.ts are deleted. deploy/lux-bank declares the bank,
lux.financial is a static site, and no host those files name resolves.


Co-authored-by: Hanzo Dev <[email protected]>
2026-09-23 05:39:03 -07:00
zeekayandHanzo AI 15131f6af4 chain letters are lowercase
CI/CD / render (push) Waiting to run
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 AI <[email protected]>
2026-09-08 15:23:20 -07:00
zeekay 13925e5e02 Own the Lux services, and only those
Zoo's cloud and its exchange services were declared here as well as in Zoo's own
repo, and the files had drifted apart. The copy here rendered a pod from a
cluster that is gone; Zoo's serves the site from files and is the one that syncs
clean. Zoo's wins because Zoo's is Zoo's.

Hanzo's operator and zen-gateway were declared here against Hanzo's own
declarations of both. Hanzo's are the ones running.

cert-manager was a third copy of one cluster-scoped install.

liquid-docs put a white-label site on a Lux host. It does not run and does not
belong in this repo. The pre-phase-a backup went with it: a snapshot of a
cluster that no longer exists, carrying the same aliases. The e2e annotation
that named that app says the same thing without it.

deploy/hanzo and deploy/hanzo-zt stay. lux-admin, lux-blog, lux-invest,
lux-safe-cgw and lux-zt-router are Lux services that sit in a namespace someone
named hanzo. The namespace is the thing worth renaming, not the owner.
2026-09-04 22:54:51 -07:00
zeekay 240534615d Drop the white-label name from a test annotation
The skip message named a white-label app that is not this estate's. The
annotation says the same thing without it.
2026-09-04 21:12:45 -07:00
zeekay 8e8bc8a4a4 feat(universe): update gateway configs and k8s manifests to /v1/chain 2026-09-04 14:29:47 -07:00
zeekayandHanzo Dev 89e2274b46 Point the gateway and the scripts at the segment luxd serves
The route table lives in configs/gateway/*.json, where each endpoint names both
the public path and the url_pattern sent upstream. Both moved: luxd answers
under /v1/chain and nothing translates between them.

The operational scripts went with it — Makefiles, the roll and validator
helpers, the e2e specs and the market-maker config all composed /v1/bc
addresses that now reach nothing. A test that dials a dead path fails in a way
that looks like the chain is down.

Lines that record an observation keep their original spelling.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-30 17:47:43 -07:00
zeekayandHanzo Dev 7417193940 A chain answers under one segment, and the fleet asks for it there
luxd moved chain APIs from /v1/bc/<alias> to /v1/chain/<alias> and deleted the
old prefix, and health moved onto typed ops at /v1/health/ops/*. Both landed in
node 1.36.181. Nothing here could move before that image existed, and the image
cannot roll out while the manifests still ask for addresses it no longer serves,
so the pin and the paths move in one commit.

What moved:
  image    1.36.171 -> 1.36.181, digest with it, on devnet/testnet/mainnet
  internal exchange-api, lux-hb2, rpc-tip-gate, the luxd startup script
  probes   /v1/health/liveness -> /v1/health/ops/liveness (bare /v1/health,
           which the local compose healthcheck used, no longer routes at all)
  public   the devnet and testnet gateway routes

Comments that RECORD an observation keep their original spelling: a note saying
/v1/bc/zoo/rpc returned 404 on a given date is a fact about that date, and
rewriting it would make the record false. Only live addresses moved.

1.36.181 also carries chains v1.7.38, so the deployed node has the zkvm
content-derived transaction identity.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-30 16:17:20 -07:00
zeekayandHanzo Dev 50646b4298 e2e: assert what 96369 says about the DAO stack
The Lux block asserted three Safe addresses had code — all three are
empty — and asserted a Governor was dead, at an address that is not the
Governor. Two tests failed and the third passed about nothing, so the
suite that exists to catch this could not.

It now reads the live Governor: deployed, counting in seconds, voting
the token deployed with it. The clock assertion is the one worth having
— the deploy was fine and unusable, because OpenZeppelin's default
block-number clock turned votingDelay/votingPeriod written as seconds
into block counts, which on a demand-driven chain also stops whenever
the chain is quiet. Only the mode says which unit those numbers are in.

The Safe absence is asserted rather than skipped, over all four
addresses the various records name. The day one has code this fails, and
that failure is the reminder to point lux/dao at it.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-29 22:58:59 -07:00
Antje WorringandHanzo AI da50d29940 e2e: fail the gate when any job it guards fails
The gate enforced only health. explorer, apps and branded each printed a
warning and fell through to an unconditional 'gate passed', so run 62877
went green with three red jobs. One rule now covers every guarded job:
green or the gate is red.

The apps matrix named itself App E2E (${{ matrix.app }}), which
stringifies the leg mapping to 'Object' — five identically named jobs,
none identifying which leg failed. Use matrix.app.name.

Explorer E2E was one of the hidden failures: it asserted SPC serves
stats, but no SPC chain exists and the explorer config disables the slug,
so api-explore-spc answers a plain-text 404. Four specs each carried
their own copy of the chain list; EXPLORERS in env.ts is now the one
list, and SPC is absent from it with the reason recorded.

httpGetJson fed error-page bodies to JSON.parse, reporting a complaint
about byte 4 of '404 page not found' instead of the endpoint and status.
It now reports both.
2026-08-28 12:39:52 -07:00
Hanzo Dev a7e59013fe e2e(cloud): the authorize request was missing its PKCE challenge
'OAuth authorize URL works with lux-cloud client' failed on all three
viewports. The endpoint was fine — the request was not. lux-cloud is a PUBLIC
client, and IAM refuses an authorize without a code_challenge:

    /auth/callback?error=invalid_request
      &error_description=PKCE+is+required+for+public+clients

That bounce is a redirect, so isRealAuthorizeResponse called it real and the
test went on to die waiting for a login form a rejected request never reaches.
The failure therefore read as 'authorize is broken' while authorize was
enforcing its contract. env.ts already documented the challenge in the URL it
had measured, and 50-login already asserts a public client must send one; this
test was the only place that did not.

Adds pkce() beside OIDC — one builder, where the OIDC contract already lives.
The verifier is fixed, not random: nothing exchanges it, and a fresh secret per
run makes a re-run non-reproducible.

The response check had to change with it. Once the request is valid the flow
lands on the real login page, which is 200 text/html — and measured today, that
page and a control request to a path that does not exist return BYTE-IDENTICAL
654-byte SPA shells. No server-side signal separates them, so this test now
asserts the one thing that does: an OAuth error never comes back to our own
callback, and the password field renders. isRealAuthorizeResponse is left alone
for 13-iam-billing, which reads it from a non-navigating request.

  cloud suite: 54 passed / 3 failed  ->  57 passed / 0 failed

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 12:38:41 -07:00
zeekayandHanzo AI b0c6e55220 e2e: the staking route is gone, so stop declaring it
The stLUX vault page was removed from the console — no vault is configured for
any brand, so the surface only ever said so, and staking returns are served by
/dashboard/calculator and /dashboard/validators from the P-Chain. lux.cloud is
a catch-all origin, so the old path still paints the console's "No such
section"; a bodyLen assertion against that shell passes and proves nothing.
It moves to NOT_SERVED, where the registry keeps retired paths visible.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 12:38:25 -07:00
zeekay b588abf446 the estate face is Zen, everywhere it is named
Two of the five explorer brands pinned NEXT_PUBLIC_FONT_FAMILY to Geist off fonts.googleapis.com: every visitor to explore.lux.network and the SPC explorer was announced to Google on page load, and the two hosts rendered in a face the other three do not use. Dropping the pair leaves all five brands with the same env and the image's own faces.

The branded matrix declared Basel Grotesk as the brand token for all four orgs, and the broker audit had already been switched to test for Zen while still calling the result hasGeist and printing it under a Geist label — a check whose name denied its own predicate. The README hero card asked for Inter.
2026-08-28 11:19:47 -07:00
zeekay 0ef5c02155 fix(type): the binding is named for the face it holds
A docs-site template repeated across these repos named three different faces in
one place: a binding called `inter` or `geist`, constructed from next/font/google
so it actually held Inter, publishing a custom property called
`--font-geist-sans` that @hanzo/font does not set. Whichever of the three a
reader trusted, two of them were wrong.

They take Zen and ZenMono from @hanzo/font — bindings named `sans` and `mono`
for the role they fill, setting --font-zen-sans and --font-zen-mono, which is
what the package publishes.
2026-08-28 11:19:46 -07:00
zeekayandHanzo Dev cda533f209 ci: use Forge-compatible artifacts and stable live gates
Co-authored-by: Hanzo Dev <[email protected]>
2026-08-21 15:51:17 -07:00
zooqueen b3da8551c1 e2e: fail the gate when any job it guards fails
The gate enforced only health. explorer, apps and branded each printed a
warning and fell through to an unconditional 'gate passed', so run 62877
went green with three red jobs. One rule now covers every guarded job:
green or the gate is red.

The apps matrix named itself App E2E (${{ matrix.app }}), which
stringifies the leg mapping to 'Object' — five identically named jobs,
none identifying which leg failed. Use matrix.app.name.

Explorer E2E was one of the hidden failures: it asserted SPC serves
stats, but no SPC chain exists and the explorer config disables the slug,
so api-explore-spc answers a plain-text 404. Four specs each carried
their own copy of the chain list; EXPLORERS in env.ts is now the one
list, and SPC is absent from it with the reason recorded.

httpGetJson fed error-page bodies to JSON.parse, reporting a complaint
about byte 4 of '404 page not found' instead of the endpoint and status.
It now reports both.
2026-08-13 18:05:17 -07:00
zeekayandHanzo Dev b92e20edd4 e2e(cloud): the authorize request was missing its PKCE challenge
'OAuth authorize URL works with lux-cloud client' failed on all three
viewports. The endpoint was fine — the request was not. lux-cloud is a PUBLIC
client, and IAM refuses an authorize without a code_challenge:

    /auth/callback?error=invalid_request
      &error_description=PKCE+is+required+for+public+clients

That bounce is a redirect, so isRealAuthorizeResponse called it real and the
test went on to die waiting for a login form a rejected request never reaches.
The failure therefore read as 'authorize is broken' while authorize was
enforcing its contract. env.ts already documented the challenge in the URL it
had measured, and 50-login already asserts a public client must send one; this
test was the only place that did not.

Adds pkce() beside OIDC — one builder, where the OIDC contract already lives.
The verifier is fixed, not random: nothing exchanges it, and a fresh secret per
run makes a re-run non-reproducible.

The response check had to change with it. Once the request is valid the flow
lands on the real login page, which is 200 text/html — and measured today, that
page and a control request to a path that does not exist return BYTE-IDENTICAL
654-byte SPA shells. No server-side signal separates them, so this test now
asserts the one thing that does: an OAuth error never comes back to our own
callback, and the password field renders. isRealAuthorizeResponse is left alone
for 13-iam-billing, which reads it from a non-navigating request.

  cloud suite: 54 passed / 3 failed  ->  57 passed / 0 failed

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-08 14:38:05 -07:00
zeekayandHanzo Dev f84d398e19 e2e: the staking route is gone, so stop declaring it
The stLUX vault page was removed from the console — no vault is configured for
any brand, so the surface only ever said so, and staking returns are served by
/dashboard/calculator and /dashboard/validators from the P-Chain. lux.cloud is
a catch-all origin, so the old path still paints the console's "No such
section"; a bodyLen assertion against that shell passes and proves nothing.
It moves to NOT_SERVED, where the registry keeps retired paths visible.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-08 05:56:54 -07:00
zeekayandHanzo Dev faf8ae7983 kms.lux.cloud is the brand host; kms.lux.network is retired
The .network host was retired 2026-07-27 and no longer resolves at all, so every
reference to it named an address that cannot answer. The brand host is .cloud,
matching kms.zoo.cloud and the rest of the white-label convention.

Left alone deliberately, because rewriting them would invert what they say: the
LLM.md line that RECORDS the retirement, the e2e spec that ASSERTS
kms.lux.network must not resolve, and the recorded applies + cluster backups,
which are faithful accounts of what was applied and are not config.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 08:17:04 -07:00
zeekayandHanzo Dev 30e162ad7b kms.lux.cloud is the brand host; kms.lux.network is retired
The .network host was retired 2026-07-27 and no longer resolves at all, so every
reference to it named an address that cannot answer. The brand host is .cloud,
matching kms.zoo.cloud and the rest of the white-label convention.

Left alone deliberately, because rewriting them would invert what they say: the
LLM.md line that RECORDS the retirement, the e2e spec that ASSERTS
kms.lux.network must not resolve, and the recorded applies + cluster backups,
which are faithful accounts of what was applied and are not config.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 08:17:04 -07:00
zeekayandHanzo Dev 0ad6ac563d e2e: one way to build an IAM authorize URL; kill the silent-pass
IAM routes exactly ONE authorize path (iam internal/oidc/oidc.go
PathAuthorize) = /v1/iam/oauth/authorize. Two specs hand-built
`${iam}/oauth/authorize` instead, and the IdP SPA answers ANY unrouted
path with 200 text/html — so both specs asserted a 2xx/3xx and PASSED
on a dead endpoint.

Measured on lux.id 2026-07-27, same query string on each:
  /v1/iam/oauth/authorize  302                              <- REAL (redirect to login)
  /v1/iam/oauth/authorize  400 text/plain  "invalid redirect_uri" (unregistered uri)
  /oauth/authorize         200 text/html   (SPA shell)
  /oauth2/authorize        200 text/html   (SPA shell)
  /zzz-control             200 text/html   (SPA shell)      <- CONTROL: 200 proves nothing
  /v1/iam/.well-known/jwks 200 application/json (real keys)

env.ts now owns the path (OIDC.authorizePath/tokenPath/jwksPath) and
exports authorizeUrl() — the same "one way" that orgRpc() already gives
RPC URLs. isRealAuthorizeResponse() rejects the shell: a 200 text/html
can no longer pass.

Proof both directions, playwright 13-iam '-g OAuth authorize endpoint':
  canonical path  -> 6 passed, 3 skipped (Zoo DNS); logs "400 text/plain"
  NEGATIVE CONTROL, authorizePath flipped back to /oauth/authorize
                  -> 2 failed (Hanzo, Lux)
The old assertion passed on that identical response.

05-cloud also gains a positive DOM assertion (a visible password input)
instead of "current URL contains lux.id", which the shell satisfies.

Also: cm/exchange-config's gateway URL. The live ConfigMap in ns
lux-mainnet still carries /mainnet/ext/bc/C/rpc and this source had been
half-converted to /mainnet/v1/bc/C/rpc — still dead. Measured:
  api.lux.network/mainnet/ext/bc/C/rpc 404
  api.lux.network/mainnet/v1/bc/C/rpc  404   <- the /mainnet/ segment is dead too
  api.lux.network/v1/bc/C/rpc          200 eth_chainId 0x17871 (96369)

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 18:08:38 -07:00
zeekayandHanzo Dev 97642216ec e2e: one way to build an IAM authorize URL; kill the silent-pass
IAM routes exactly ONE authorize path (iam internal/oidc/oidc.go
PathAuthorize) = /v1/iam/oauth/authorize. Two specs hand-built
`${iam}/oauth/authorize` instead, and the IdP SPA answers ANY unrouted
path with 200 text/html — so both specs asserted a 2xx/3xx and PASSED
on a dead endpoint.

Measured on lux.id 2026-07-27, same query string on each:
  /v1/iam/oauth/authorize  302                              <- REAL (redirect to login)
  /v1/iam/oauth/authorize  400 text/plain  "invalid redirect_uri" (unregistered uri)
  /oauth/authorize         200 text/html   (SPA shell)
  /oauth2/authorize        200 text/html   (SPA shell)
  /zzz-control             200 text/html   (SPA shell)      <- CONTROL: 200 proves nothing
  /v1/iam/.well-known/jwks 200 application/json (real keys)

env.ts now owns the path (OIDC.authorizePath/tokenPath/jwksPath) and
exports authorizeUrl() — the same "one way" that orgRpc() already gives
RPC URLs. isRealAuthorizeResponse() rejects the shell: a 200 text/html
can no longer pass.

Proof both directions, playwright 13-iam '-g OAuth authorize endpoint':
  canonical path  -> 6 passed, 3 skipped (Zoo DNS); logs "400 text/plain"
  NEGATIVE CONTROL, authorizePath flipped back to /oauth/authorize
                  -> 2 failed (Hanzo, Lux)
The old assertion passed on that identical response.

05-cloud also gains a positive DOM assertion (a visible password input)
instead of "current URL contains lux.id", which the shell satisfies.

Also: cm/exchange-config's gateway URL. The live ConfigMap in ns
lux-mainnet still carries /mainnet/ext/bc/C/rpc and this source had been
half-converted to /mainnet/v1/bc/C/rpc — still dead. Measured:
  api.lux.network/mainnet/ext/bc/C/rpc 404
  api.lux.network/mainnet/v1/bc/C/rpc  404   <- the /mainnet/ segment is dead too
  api.lux.network/v1/bc/C/rpc          200 eth_chainId 0x17871 (96369)

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 18:08:38 -07:00
zeekayandHanzo Dev 11c60378fe test(e2e): let the economy screens finish reading the chain before judging them
The nine screens the owner named read the chain, and the render check clears at
2.5s. MEASURED on /dashboard/treasury at 3/8/15/25/40s: at 2.5s it still shows
"…" and "Reading token balances…", and it has settled by 3s — no permanent
skeleton, just a check that was looking too early. So the evidence screenshots
were of a half-loaded page and the chain-context assertion was reading an
ellipsis where the address had not arrived yet.

One 3s settle in the economy block only. It does not soften the assertion —
/dashboard/{safes,mpc,bridge,dex,deploy} still show no network, chainId,
address or height at 3/3 viewports.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 20:55:45 -07:00
zeekayandHanzo Dev eb5176cc01 test(e2e): let the economy screens finish reading the chain before judging them
The nine screens the owner named read the chain, and the render check clears at
2.5s. MEASURED on /dashboard/treasury at 3/8/15/25/40s: at 2.5s it still shows
"…" and "Reading token balances…", and it has settled by 3s — no permanent
skeleton, just a check that was looking too early. So the evidence screenshots
were of a half-loaded page and the chain-context assertion was reading an
ellipsis where the address had not arrived yet.

One 3s settle in the economy block only. It does not soften the assertion —
/dashboard/{safes,mpc,bridge,dex,deploy} still show no network, chainId,
address or height at 3/3 viewports.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 20:55:45 -07:00
zeekayandHanzo Dev bc3b88c0b0 test(e2e): cover the operator console at /admin, positively
Ten screens plus the value boundary, per-network, asserted by what the page
CONTAINS. Two false passes in one day came from asserting the absence of an
error string — a blank page, a 404 body and a login redirect all contain no
error text. Every assertion here names a section title, a chain ID or a live
figure, so a page that failed to render fails the test.

The first case, "the operator console is reachable", is RED until cutover and
must stay that way: the console is built and committed (bootnode/bootnode
efe3b9d) but the image carrying /admin is not published and the Ingress pointing
admin.lux.cloud at it is owner-gated. The remaining cases skip while it 404s,
so the suite reports one honest finding rather than fifteen copies of it.

ADMIN_BASE points the suite at a local static export during development.
admin.lux.cloud is deliberately not the default: it is behind admin-guard, so an
unauthenticated run would be asserting the guard, not the console.

Beyond rendering, the spec pins the two properties the console exists to have:
every value-moving control opens a preview that names its authority, its
parameters and its stop, and confirming yields a document that says
"executed": false — no signature is collected anywhere; and the risk ledger
still names degree-0 MPC shares, 0.0000 uptimes, the dead graph layer, devnet's
missing Access-Control-Allow-Origin and "check the image tag". A console that
stops surfacing those has regressed into the thing it replaced.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 20:41:55 -07:00
zeekayandHanzo Dev 7ca0169fd4 test(e2e): cover the operator console at /admin, positively
Ten screens plus the value boundary, per-network, asserted by what the page
CONTAINS. Two false passes in one day came from asserting the absence of an
error string — a blank page, a 404 body and a login redirect all contain no
error text. Every assertion here names a section title, a chain ID or a live
figure, so a page that failed to render fails the test.

The first case, "the operator console is reachable", is RED until cutover and
must stay that way: the console is built and committed (bootnode/bootnode
efe3b9d) but the image carrying /admin is not published and the Ingress pointing
admin.lux.cloud at it is owner-gated. The remaining cases skip while it 404s,
so the suite reports one honest finding rather than fifteen copies of it.

ADMIN_BASE points the suite at a local static export during development.
admin.lux.cloud is deliberately not the default: it is behind admin-guard, so an
unauthenticated run would be asserting the guard, not the console.

Beyond rendering, the spec pins the two properties the console exists to have:
every value-moving control opens a preview that names its authority, its
parameters and its stop, and confirming yields a document that says
"executed": false — no signature is collected anywhere; and the risk ledger
still names degree-0 MPC shares, 0.0000 uptimes, the dead graph layer, devnet's
missing Access-Control-Allow-Origin and "check the image tag". A console that
stops surfacing those has regressed into the thing it replaced.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 20:41:55 -07:00
zeekayandHanzo Dev d9b5f7a9a3 test(e2e): assert the chain is MOVING, and open every page of every product
The suite was green while mainnet had not produced a block in 59 hours.

Nothing in it asked whether the chain was alive. `00-health` has nine tests
that all reduce to `eth_blockNumber > 0` — "node reports healthy", "C-Chain is
bootstrapped", "all primary chains bootstrapped" — and a frozen chain answers
every one of them. Readiness agreed: 15 pods at 5/5 Ready, every public API
200, on all three networks, one of which was stopped.

  mainnet 96369  tip 1,098,191  age 58.9h   testnet 96368  tip 1,354  age 3.6m
  devnet  96367  tip       528  age  3.3m

e2e/00-liveness.spec.ts gates on the TIP. A fixed "block every N seconds" is
wrong for demand-driven chains, so the gate CALIBRATES ITSELF: the tip may be
no older than 4x the chain's own median gap over its last 8 blocks, floored at
600s. Measured cadences are 7s on mainnet and ~300s on testnet/devnet (the
heartbeat CronJob) — no constant spans both. Median, not mean: one 27,375s gap
IS the freeze, and a mean would let a stall widen its own budget.

Four orthogonal tests: fresh, monotonic (the tip never goes backwards and a
served block keeps its hash — the reorg shape devnet had at height 512),
consistent (net_version vs NETWORKS.yaml), and honest — a full validator set
must imply a fresh tip, which asserts the readiness lie head-on. Mainnet is RED
on three of them. That is the correct result; do not widen the budget.

Coverage was a function of which spec somebody happened to write: Safe's 31
routes, MPC's 15 and the whole admin surface had ZERO tests between them while
api.lux.financial, whose API is dead, carried 293. So routes become DATA —
e2e/routes.ts, 156 declared pages across 11 surfaces, iterated by 60-surfaces
(anonymous) and 61-console (signed in). A new page is one line there.

HTTP status is not evidence: SEVEN of the nine origins answer 200 for a path
that does not exist (measured with a /zzz-bogus control each; only Safe and the
explorer 404 honestly, and those two get a live control test every run). Every
route is therefore opened in a browser and must have PAINTED — real text into a
non-empty root, AND not the app's error boundary. Two assertions, because a
crash screen sails through a length check: exchange /pool is 200 with 553
characters that all say "Something went wrong". One reload before failing,
which is exactly the line between a page with no retry and a broken page.

First run found, each with a screenshot:
  exchange /positions /pool /pool/v2 /bridge /teleport  crash screen at 200
  exchange /vote                                        white screen, 0 chars
  explorer /tokens /stats /advanced-filter              crash screen at 200
  console /dashboard/{safes,mpc,bridge,dex,deploy}      no network, chainId,
    address or height anywhere on screen — five of the nine surfaces the owner
    named, and nothing on them tells an operator which chain they are on

Also fixed, all of them tests that could not fail:
- Peer connectivity was `catch { console.log('firewalled — PASS'); return }`.
  /v1/info is not uniformly firewalled, it is inconsistently deployed (POST:
  testnet 200, mainnet 404, devnet 404). Now asserts mesh depth wherever it is
  served and falls back to the P-Chain validator set, which a single-node
  network fails.
- 02-exchange and 20-exchange-deep skipped 58 cases behind lux.exchange, whose
  TLS handshake fails from this network in curl, python and chromium alike.
  exchange.lux.network is the same deployment at 200 on all 22 routes.
  helpers.exchangeBase() picks the door that answers; the vanity domain being
  down is now one deliberate assertion instead of a silence over 22 pages.
- 13-iam asserted a hand-built /.well-known/jwks that returns 200 text/html —
  the SPA catch-all — so JSON.parse threw. Follows the discovery document now,
  which declares /v1/iam/.well-known/jwks, and checks the content-type.
- 50-login sliced userinfo to 300 chars and then parsed it, so a real sign-in
  failed on "Unterminated string in JSON" AFTER proving IAM accepted the token.
  Parsed in-page now, three claims out, nothing else in the log.

Login proven end to end: [email protected] -> lux.id -> /dashboard, iss=lux.id,
aud=lux-cloud-org-lux, userinfo 200, sign-out present. 3/3.

Read-only throughout. Every screen in 61-console can move value — treasury,
safes, mpc, bridge. It navigates and screenshots. No form submitted, nothing
prepared, nothing signed, nothing broadcast, nothing re-keyed.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 20:20:52 -07:00
zeekayandHanzo Dev 001133858b test(e2e): assert the chain is MOVING, and open every page of every product
The suite was green while mainnet had not produced a block in 59 hours.

Nothing in it asked whether the chain was alive. `00-health` has nine tests
that all reduce to `eth_blockNumber > 0` — "node reports healthy", "C-Chain is
bootstrapped", "all primary chains bootstrapped" — and a frozen chain answers
every one of them. Readiness agreed: 15 pods at 5/5 Ready, every public API
200, on all three networks, one of which was stopped.

  mainnet 96369  tip 1,098,191  age 58.9h   testnet 96368  tip 1,354  age 3.6m
  devnet  96367  tip       528  age  3.3m

e2e/00-liveness.spec.ts gates on the TIP. A fixed "block every N seconds" is
wrong for demand-driven chains, so the gate CALIBRATES ITSELF: the tip may be
no older than 4x the chain's own median gap over its last 8 blocks, floored at
600s. Measured cadences are 7s on mainnet and ~300s on testnet/devnet (the
heartbeat CronJob) — no constant spans both. Median, not mean: one 27,375s gap
IS the freeze, and a mean would let a stall widen its own budget.

Four orthogonal tests: fresh, monotonic (the tip never goes backwards and a
served block keeps its hash — the reorg shape devnet had at height 512),
consistent (net_version vs NETWORKS.yaml), and honest — a full validator set
must imply a fresh tip, which asserts the readiness lie head-on. Mainnet is RED
on three of them. That is the correct result; do not widen the budget.

Coverage was a function of which spec somebody happened to write: Safe's 31
routes, MPC's 15 and the whole admin surface had ZERO tests between them while
api.lux.financial, whose API is dead, carried 293. So routes become DATA —
e2e/routes.ts, 156 declared pages across 11 surfaces, iterated by 60-surfaces
(anonymous) and 61-console (signed in). A new page is one line there.

HTTP status is not evidence: SEVEN of the nine origins answer 200 for a path
that does not exist (measured with a /zzz-bogus control each; only Safe and the
explorer 404 honestly, and those two get a live control test every run). Every
route is therefore opened in a browser and must have PAINTED — real text into a
non-empty root, AND not the app's error boundary. Two assertions, because a
crash screen sails through a length check: exchange /pool is 200 with 553
characters that all say "Something went wrong". One reload before failing,
which is exactly the line between a page with no retry and a broken page.

First run found, each with a screenshot:
  exchange /positions /pool /pool/v2 /bridge /teleport  crash screen at 200
  exchange /vote                                        white screen, 0 chars
  explorer /tokens /stats /advanced-filter              crash screen at 200
  console /dashboard/{safes,mpc,bridge,dex,deploy}      no network, chainId,
    address or height anywhere on screen — five of the nine surfaces the owner
    named, and nothing on them tells an operator which chain they are on

Also fixed, all of them tests that could not fail:
- Peer connectivity was `catch { console.log('firewalled — PASS'); return }`.
  /v1/info is not uniformly firewalled, it is inconsistently deployed (POST:
  testnet 200, mainnet 404, devnet 404). Now asserts mesh depth wherever it is
  served and falls back to the P-Chain validator set, which a single-node
  network fails.
- 02-exchange and 20-exchange-deep skipped 58 cases behind lux.exchange, whose
  TLS handshake fails from this network in curl, python and chromium alike.
  exchange.lux.network is the same deployment at 200 on all 22 routes.
  helpers.exchangeBase() picks the door that answers; the vanity domain being
  down is now one deliberate assertion instead of a silence over 22 pages.
- 13-iam asserted a hand-built /.well-known/jwks that returns 200 text/html —
  the SPA catch-all — so JSON.parse threw. Follows the discovery document now,
  which declares /v1/iam/.well-known/jwks, and checks the content-type.
- 50-login sliced userinfo to 300 chars and then parsed it, so a real sign-in
  failed on "Unterminated string in JSON" AFTER proving IAM accepted the token.
  Parsed in-page now, three claims out, nothing else in the log.

Login proven end to end: [email protected] -> lux.id -> /dashboard, iss=lux.id,
aud=lux-cloud-org-lux, userinfo 200, sign-out present. 3/3.

Read-only throughout. Every screen in 61-console can move value — treasury,
safes, mpc, bridge. It navigates and screenshots. No form submitted, nothing
prepared, nothing signed, nothing broadcast, nothing re-keyed.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 20:20:52 -07:00
zeekayandHanzo Dev 0d9fceaf16 test(e2e): capture and assert the institutional console, three viewports
The companion to 51-institutional-baseline: the same screen list, run
against the rebuilt console, so every "after" frame has a "before" to sit
beside.

It is a test, not a slideshow. It asserts positively — what must BE on the
page, never the absence of an error string:

  • no screen renders blank (five used to be white voids)
  • the page body never scrolls sideways at any viewport
  • nothing is UNREACHABLE outside the viewport. "Outside the viewport" is
    not the same as clipped — a cell inside a horizontal scroller can be
    swiped to. The check walks up for a scrollable ancestor first, so it
    catches the real defect (Safes putting THRESHOLD and OWNER off a 390px
    screen with no affordance) and not every wide table.
  • no bare unexplained zero: a `0` counts as a defect only when nothing
    near it says idle / empty / none / unknown / unavailable
  • every figure shown carries provenance
  • the screens that state money or custody render through the shared
    primitive, which is what makes a bare number impossible on them

Two caveats are written on the file rather than left to be discovered.
The chain data is real and live — balances, heights, validators, Safe
owners and thresholds are read from mainnet at capture time. The SESSION
is synthetic: lux.id's redirect_uri is https://lux.cloud/auth/callback, so
a local build cannot complete an OIDC round trip, and a fake token is
seeded into the sessionStorage keys the IAM SDK reads (it checks expiry and
calls userinfo; it does not verify the signature client-side). This spec
therefore proves nothing about authentication — 12-iam-login.spec.ts does
that, against the deployed surface.

Read-only. It signs nothing and submits nothing.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 20:18:32 -07:00
zeekayandHanzo Dev 1579d10978 test(e2e): capture and assert the institutional console, three viewports
The companion to 51-institutional-baseline: the same screen list, run
against the rebuilt console, so every "after" frame has a "before" to sit
beside.

It is a test, not a slideshow. It asserts positively — what must BE on the
page, never the absence of an error string:

  • no screen renders blank (five used to be white voids)
  • the page body never scrolls sideways at any viewport
  • nothing is UNREACHABLE outside the viewport. "Outside the viewport" is
    not the same as clipped — a cell inside a horizontal scroller can be
    swiped to. The check walks up for a scrollable ancestor first, so it
    catches the real defect (Safes putting THRESHOLD and OWNER off a 390px
    screen with no affordance) and not every wide table.
  • no bare unexplained zero: a `0` counts as a defect only when nothing
    near it says idle / empty / none / unknown / unavailable
  • every figure shown carries provenance
  • the screens that state money or custody render through the shared
    primitive, which is what makes a bare number impossible on them

Two caveats are written on the file rather than left to be discovered.
The chain data is real and live — balances, heights, validators, Safe
owners and thresholds are read from mainnet at capture time. The SESSION
is synthetic: lux.id's redirect_uri is https://lux.cloud/auth/callback, so
a local build cannot complete an OIDC round trip, and a fake token is
seeded into the sessionStorage keys the IAM SDK reads (it checks expiry and
calls userinfo; it does not verify the signature client-side). This spec
therefore proves nothing about authentication — 12-iam-login.spec.ts does
that, against the deployed surface.

Read-only. It signs nothing and submits nothing.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 20:18:32 -07:00
zeekayandHanzo Dev 1aec218aa7 test(e2e): measure the institutional baseline on lux.cloud, all 27 screens
The owner's ask is a Fireblocks/Alchemy-grade experience for institutional
investors. That is unmeasurable as an opinion, so this walks the whole
signed-in surface and records the six things such a console is actually
judged on — renders / says what it is / explains its zeros / had its data
calls succeed / finished loading / can be verified against the chain — at
all three viewports, with the evidence written next to the screenshots.

What it found on the deployed console (api.lux.network, mainnet):

  • /dashboard/dex at 390px never finishes rendering. It fans out ~55
    parallel eth_calls from the browser and 26 of them come back 429, so
    the page sits on "Reading pools, reserves, and markets from chain…".
    Analytics does the same with ~60. The two most data-dense screens are
    therefore non-deterministic — 2567ch at laptop, 701ch at mobile.
  • Seven routes silently render the Overview instead of themselves
    (api-keys, wallets, team, settings, webhooks, usage, assets). The
    router's catch-all redirects to /dashboard, so a bookmarked
    key-management URL lands on a page that looks legitimate and is not
    the one that was asked for. A 404 would be information; this is not.

Both are asserted, not just reported — they are the two defects here that
are never a judgement call. Everything else the run measures is a design
opinion and belongs in the review.

Two things were decomplected to make room for it rather than copied:

  • signIn() moves into helpers. 50-login.spec.ts remains the PROOF that
    the flow is real (token claims, userinfo 200, sign-out present); every
    other authenticated spec now just uses it. A second copy of the click
    path is how a run silently ends up at github.com/login when a button's
    accessible name changes.
  • Screenshots gain a SET (LUX_SHOT_SET), defaulting to the env name, so
    a deliberate one-off capture reuses the existing shotPath machinery
    instead of growing a second naming scheme beside it.

Measured, not assumed: the first version of the stuck-loading detector
only matched loading/fetching/please-wait, and it passed the DEX page at
390px while the screenshot plainly said "Reading … from chain…". A
detector that misses the real copy is just a slower way of not looking.

Read-only throughout. It signs in and it looks; it never submits a form,
never signs and never broadcasts.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 18:44:02 -07:00
zeekayandHanzo Dev c16dfa1f56 test(e2e): measure the institutional baseline on lux.cloud, all 27 screens
The owner's ask is a Fireblocks/Alchemy-grade experience for institutional
investors. That is unmeasurable as an opinion, so this walks the whole
signed-in surface and records the six things such a console is actually
judged on — renders / says what it is / explains its zeros / had its data
calls succeed / finished loading / can be verified against the chain — at
all three viewports, with the evidence written next to the screenshots.

What it found on the deployed console (api.lux.network, mainnet):

  • /dashboard/dex at 390px never finishes rendering. It fans out ~55
    parallel eth_calls from the browser and 26 of them come back 429, so
    the page sits on "Reading pools, reserves, and markets from chain…".
    Analytics does the same with ~60. The two most data-dense screens are
    therefore non-deterministic — 2567ch at laptop, 701ch at mobile.
  • Seven routes silently render the Overview instead of themselves
    (api-keys, wallets, team, settings, webhooks, usage, assets). The
    router's catch-all redirects to /dashboard, so a bookmarked
    key-management URL lands on a page that looks legitimate and is not
    the one that was asked for. A 404 would be information; this is not.

Both are asserted, not just reported — they are the two defects here that
are never a judgement call. Everything else the run measures is a design
opinion and belongs in the review.

Two things were decomplected to make room for it rather than copied:

  • signIn() moves into helpers. 50-login.spec.ts remains the PROOF that
    the flow is real (token claims, userinfo 200, sign-out present); every
    other authenticated spec now just uses it. A second copy of the click
    path is how a run silently ends up at github.com/login when a button's
    accessible name changes.
  • Screenshots gain a SET (LUX_SHOT_SET), defaulting to the env name, so
    a deliberate one-off capture reuses the existing shotPath machinery
    instead of growing a second naming scheme beside it.

Measured, not assumed: the first version of the stuck-loading detector
only matched loading/fetching/please-wait, and it passed the DEX page at
390px while the screenshot plainly said "Reading … from chain…". A
detector that misses the real copy is just a slower way of not looking.

Read-only throughout. It signs in and it looks; it never submits a form,
never signs and never broadcasts.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 18:44:02 -07:00
zeekayandHanzo Dev e2503dae65 e2e: one environment model, viewport projects, and a login proof that can fail
LUX_ENV=local|devnet|testnet|mainnet is now the only environment knob. e2e/env.ts
resolves every base URL, RPC endpoint, network ID and chain ID from NETWORKS.yaml
at runtime; playwright.config.ts and helpers.ts derive from it instead of keeping
their own copies. Those copies had already drifted — the config resolved the
testnet explorer to test-explore.lux.network, which is NXDOMAIN, while helpers.ts
used the real explore-test.lux.network. The retired two-axis ENV/LUX_NETWORK
model now fails loudly rather than being half-honoured.

Projects are viewports (mobile 390x844, tablet 834x1194, laptop 1440x900), so
"mobile and desktop" is the same three frames every run. Screenshots go to
~/Desktop/lux-e2e/<env>/<surface>-<viewport>.png on pass, and shot-reporter.ts
copies a failing test's dying state next to them — a failure you cannot look at
is a failure you cannot fix.

50-login.spec.ts proves sign-in for real: lux.cloud -> lux.id -> /dashboard, with
the password from LUX_E2E_PASSWORD only. It refuses the cheap answers, because
each one lies here — /dashboard is an SPA that returns 200 and keeps its URL when
anonymous, and /auth/callback renders "Sign-in failed" at 200. The proof is the
token: a JWT in sessionStorage whose iss is the IdP and whose email is the
account typed, which lux.id/v1/iam/oauth/userinfo accepts (401 bare, 401 for a
junk token). That is the leg that breaks under invalid_client. It also asserts
the handoff is a public PKCE client and carries a negative control so the
positive assertions cannot go vacuous. Verified with a wrong password: red.

Measured while building this, not assumed:
  - workers are per-env. Mainnet is CDN-fronted and ran the gate at 4 with
    342/342; testnet hits the raw LB and lost 66 cases to throttling at 4 that
    all passed at 2.
  - `locator('text=/block/i').first()` in two specs resolved to a hidden Radix
    menu trigger, so both tracked the nav's collapse rather than the blocks
    table. Replaced by one shared assertion on the section AND a block link.
  - the token check runs inside the page: page.request.get attaches request
    headers to a thrown error, which printed a live bearer token into the log.
  - 40-lux-financial carried a plaintext password; it now comes from the
    environment.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:04 -07:00
zeekayandHanzo Dev f8895b9a54 e2e: one environment model, viewport projects, and a login proof that can fail
LUX_ENV=local|devnet|testnet|mainnet is now the only environment knob. e2e/env.ts
resolves every base URL, RPC endpoint, network ID and chain ID from NETWORKS.yaml
at runtime; playwright.config.ts and helpers.ts derive from it instead of keeping
their own copies. Those copies had already drifted — the config resolved the
testnet explorer to test-explore.lux.network, which is NXDOMAIN, while helpers.ts
used the real explore-test.lux.network. The retired two-axis ENV/LUX_NETWORK
model now fails loudly rather than being half-honoured.

Projects are viewports (mobile 390x844, tablet 834x1194, laptop 1440x900), so
"mobile and desktop" is the same three frames every run. Screenshots go to
~/Desktop/lux-e2e/<env>/<surface>-<viewport>.png on pass, and shot-reporter.ts
copies a failing test's dying state next to them — a failure you cannot look at
is a failure you cannot fix.

50-login.spec.ts proves sign-in for real: lux.cloud -> lux.id -> /dashboard, with
the password from LUX_E2E_PASSWORD only. It refuses the cheap answers, because
each one lies here — /dashboard is an SPA that returns 200 and keeps its URL when
anonymous, and /auth/callback renders "Sign-in failed" at 200. The proof is the
token: a JWT in sessionStorage whose iss is the IdP and whose email is the
account typed, which lux.id/v1/iam/oauth/userinfo accepts (401 bare, 401 for a
junk token). That is the leg that breaks under invalid_client. It also asserts
the handoff is a public PKCE client and carries a negative control so the
positive assertions cannot go vacuous. Verified with a wrong password: red.

Measured while building this, not assumed:
  - workers are per-env. Mainnet is CDN-fronted and ran the gate at 4 with
    342/342; testnet hits the raw LB and lost 66 cases to throttling at 4 that
    all passed at 2.
  - `locator('text=/block/i').first()` in two specs resolved to a hidden Radix
    menu trigger, so both tracked the nav's collapse rather than the blocks
    table. Replaced by one shared assertion on the section AND a block link.
  - the token check runs inside the page: page.request.get attaches request
    headers to a thrown error, which printed a live bearer token into the log.
  - 40-lux-financial carried a plaintext password; it now comes from the
    environment.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:04 -07:00
zeekay b920b06fcb chore: sync working tree
Commits 2 outstanding change(s) that were sitting uncommitted.
No build artifacts and no secrets in the changeset (both checked).
2026-07-26 15:46:04 -07:00
zeekay fd5d91cb0c chore: sync working tree
Commits 2 outstanding change(s) that were sitting uncommitted.
No build artifacts and no secrets in the changeset (both checked).
2026-07-26 15:46:04 -07:00
zeekayandHanzo Dev 8adc86dbf6 test(e2e): clear fixed bridge + zoo-cloud flags (self-cleaning)
Deployed + verified: bridge-zoo (font, leak, console) and bridge-pars
(font, leak) now pass at all 4 breakpoints — Basel Grotesk webfont loads,
description meta is white-labeled per brand, and networks load same-origin
(no cross-brand CORS). zoo-cloud overflow is gone (cloud-web v1.3.2). Only
bridge-lux font remains — its host runs a stale Go all-in-one image; flag
reworded with the real flip condition.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:04 -07:00
zeekayandHanzo Dev 5dc2ca30a6 test(e2e): clear fixed bridge + zoo-cloud flags (self-cleaning)
Deployed + verified: bridge-zoo (font, leak, console) and bridge-pars
(font, leak) now pass at all 4 breakpoints — Basel Grotesk webfont loads,
description meta is white-labeled per brand, and networks load same-origin
(no cross-brand CORS). zoo-cloud overflow is gone (cloud-web v1.3.2). Only
bridge-lux font remains — its host runs a stale Go all-in-one image; flag
reworded with the real flip condition.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:04 -07:00
zeekayandHanzo Dev fc0351fb86 test(e2e): branded web3-surface matrix — live · functional · branded @ 4 breakpoints
Durable regression suite proving every public surface across Lux/Zoo/Hanzo/Pars
(explorers, wallets, id, votes, work boards, bridges, safe, clouds, exchanges)
is live, functional, and correctly branded at 390/768/1280/1920 — real
Chrome/Blink (channel:'chrome'), read-only (no txns, no wallet-connect, no auth;
the login form is asserted, never submitted).

- e2e/branded/surfaces.ts   — ONE source-of-truth table (surface × posture × flags);
                              breakpoint-scoped flags carry a reason + flip condition
- e2e/branded/branded.spec.ts — table-driven 8-check battery (the owner's 6-point
                              rubric: 200 · branded head + zero cross-brand leak ·
                              no overflow · no app-console-errors · key control
                              visible · brand font); gate vs flag vs stale-flag
- e2e/branded/matrix-reporter.ts — renders the surface × breakpoint PASS/FLAG/FAIL
                              grid + defect ledger; writes branded-report/matrix.{md,txt,json}
- playwright.branded.config.ts — breakpoints as projects, chrome channel, matrix reporter
- e2e.yml `branded` job + README + LLM.md section

Baseline 104 cells: 67 pass · 37 flagged (each with a flip condition) · 0
regressions. Only a NEW un-flagged failure turns the run red. Screenshots →
~/Desktop/lux-shots-e2e/. Bumps @luxfi/universe 1.0.1 → 1.0.2.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:04 -07:00
zeekayandHanzo Dev 6efce1c2d2 test(e2e): branded web3-surface matrix — live · functional · branded @ 4 breakpoints
Durable regression suite proving every public surface across Lux/Zoo/Hanzo/Pars
(explorers, wallets, id, votes, work boards, bridges, safe, clouds, exchanges)
is live, functional, and correctly branded at 390/768/1280/1920 — real
Chrome/Blink (channel:'chrome'), read-only (no txns, no wallet-connect, no auth;
the login form is asserted, never submitted).

- e2e/branded/surfaces.ts   — ONE source-of-truth table (surface × posture × flags);
                              breakpoint-scoped flags carry a reason + flip condition
- e2e/branded/branded.spec.ts — table-driven 8-check battery (the owner's 6-point
                              rubric: 200 · branded head + zero cross-brand leak ·
                              no overflow · no app-console-errors · key control
                              visible · brand font); gate vs flag vs stale-flag
- e2e/branded/matrix-reporter.ts — renders the surface × breakpoint PASS/FLAG/FAIL
                              grid + defect ledger; writes branded-report/matrix.{md,txt,json}
- playwright.branded.config.ts — breakpoints as projects, chrome channel, matrix reporter
- e2e.yml `branded` job + README + LLM.md section

Baseline 104 cells: 67 pass · 37 flagged (each with a flip condition) · 0
regressions. Only a NEW un-flagged failure turns the run red. Screenshots →
~/Desktop/lux-shots-e2e/. Bumps @luxfi/universe 1.0.1 → 1.0.2.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:04 -07:00
zeekayandHanzo Dev d08c32bfd2 e2e(dao-onchain): point Zoo + Pars at the GO-LIVE work-market addresses
Swap the Zoo (200200) and Pars (494949) Bounty/Reputation constants to the newly
deployed DAO-Safe-owned work-market (supersedes the smoke set). Selectors
(bountyCount/stateOf/treasury/writer) unchanged — addresses only. Lux 96369 untouched.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00
zeekayandHanzo Dev d39441170f e2e(dao-onchain): point Zoo + Pars at the GO-LIVE work-market addresses
Swap the Zoo (200200) and Pars (494949) Bounty/Reputation constants to the newly
deployed DAO-Safe-owned work-market (supersedes the smoke set). Selectors
(bountyCount/stateOf/treasury/writer) unchanged — addresses only. Lux 96369 untouched.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00
zeekayandHanzo Dev 11912c2469 screenshots: add lux-cloud console surface + fix harness sign-in for @hanzo/gui
Add console.lux.cloud (Lux Cloud Console — the web3/infra console app) as a
lux surface with its real route list (networks, nodes, gateway, api-keys,
tokens, indexer, oracles, wallet, settlement, usage/billing, kms/secrets, …)
and the valid lux.id password-grant login ([email protected]).

Harness login() now waits for the sign-in form and matches Tamagui inputs
(autocomplete="email" / placeholder="Email"), and treats /signin as a login
gate — without this the harness could not fill or detect this console's form.

Capture currently yields the /signin gate only: login is blocked IAM-side —
the lux-cloud app in lux.id is provisioned for host cloud.lux.network, so
console.lux.cloud/auth/callback is not an allowed redirect URI. Durable fix
(separate, out of this change): add console.lux.cloud to the `cloud` app hosts
in infra/k8s/iam/provision.yaml and reconcile IAM.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00
zeekayandHanzo Dev 82797cc03b screenshots: add lux-cloud console surface + fix harness sign-in for @hanzo/gui
Add console.lux.cloud (Lux Cloud Console — the web3/infra console app) as a
lux surface with its real route list (networks, nodes, gateway, api-keys,
tokens, indexer, oracles, wallet, settlement, usage/billing, kms/secrets, …)
and the valid lux.id password-grant login ([email protected]).

Harness login() now waits for the sign-in form and matches Tamagui inputs
(autocomplete="email" / placeholder="Email"), and treats /signin as a login
gate — without this the harness could not fill or detect this console's form.

Capture currently yields the /signin gate only: login is blocked IAM-side —
the lux-cloud app in lux.id is provisioned for host cloud.lux.network, so
console.lux.cloud/auth/callback is not an allowed redirect URI. Durable fix
(separate, out of this change): add console.lux.cloud to the `cloud` app hosts
in infra/k8s/iam/provision.yaml and reconcile IAM.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00
zeekayandHanzo Dev 78ee638610 test(e2e): correct work.lux/work.hanzo skip-gate — now serve (FIXTURE), not unreachable
work.lux / work.hanzo came up during rollout: they now serve the Dework shell
(HTTP 200) but render the amber FIXTURE fallback (owner 0x0000…0000 + "Missing or
invalid parameters") because their C-Chain work-market is undeployed (brands.ts
bounty == 0x0 — Lux frozen at flag-day tip, Hanzo key-gap). Keep them skip-gated
(un-skipping would fail the emerald-LIVE invariant), but fix the stale
"HTTP-unreachable" reason to the accurate go-live condition: flip when BountyV1 is
deployed on the org C-Chain and the board shows the emerald LIVE badge.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00
zeekayandHanzo Dev 2aab9f6e87 test(e2e): correct work.lux/work.hanzo skip-gate — now serve (FIXTURE), not unreachable
work.lux / work.hanzo came up during rollout: they now serve the Dework shell
(HTTP 200) but render the amber FIXTURE fallback (owner 0x0000…0000 + "Missing or
invalid parameters") because their C-Chain work-market is undeployed (brands.ts
bounty == 0x0 — Lux frozen at flag-day tip, Hanzo key-gap). Keep them skip-gated
(un-skipping would fail the emerald-LIVE invariant), but fix the stale
"HTTP-unreachable" reason to the accurate go-live condition: flip when BountyV1 is
deployed on the org C-Chain and the board shows the emerald LIVE badge.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00
zeekayandHanzo Dev feb1105fc5 test(e2e): reflect live governance surfaces — Dework work-boards + hanzo.vote guard
32-work-boards: work.zoo + work.pars now both serve the redesigned Dework board
(luxfi/work-board). Replace the stale literal assertions (BountyV1 / #0 / "N
bounties" — removed by the redesign, the 3 false failures) with the real structural
contract via one parameterized factory: Dework chrome (icon-rail + w-64 <aside>
sidebar + Overview / Community Suggestions / Leaderboards / Combined Board nav),
inline-<svg> BrandMark (the logo is not an <img>), the Bounties Space-category grid,
the To Do / In Progress / In Review / Done kanban columns, the emerald LIVE badge
(not the amber FIXTURE fallback) + org chainId, and a UI<->chain check (the rendered
treasury truncation equals on-chain BountyV1.treasury()). Un-skip Pars (now live,
chain 494949); Lux/Hanzo stay skip-gated (HTTP-unreachable, brands.ts bounty==0x0).

31-vote: the vote shells are retiring -> work.<org>.network. Arm a hanzo.vote
shell-health guard that hammers cold loads and fails on the intermittent blank-#root
white-screen (Buffer-crash class) OR the "Made with love by Lux" cross-brand leak —
a real, known-flagged regression, kept armed (not muted/skipped). lux/zoo/pars green.

work-boards: 1 -> 10 passing (3 false failures fixed, Pars un-skipped, 2 skip).
vote: hanzo.vote deterministically flagged; lux/zoo/pars (6) green, 4 skip.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00
zeekayandHanzo Dev 66f8705e89 test(e2e): reflect live governance surfaces — Dework work-boards + hanzo.vote guard
32-work-boards: work.zoo + work.pars now both serve the redesigned Dework board
(luxfi/work-board). Replace the stale literal assertions (BountyV1 / #0 / "N
bounties" — removed by the redesign, the 3 false failures) with the real structural
contract via one parameterized factory: Dework chrome (icon-rail + w-64 <aside>
sidebar + Overview / Community Suggestions / Leaderboards / Combined Board nav),
inline-<svg> BrandMark (the logo is not an <img>), the Bounties Space-category grid,
the To Do / In Progress / In Review / Done kanban columns, the emerald LIVE badge
(not the amber FIXTURE fallback) + org chainId, and a UI<->chain check (the rendered
treasury truncation equals on-chain BountyV1.treasury()). Un-skip Pars (now live,
chain 494949); Lux/Hanzo stay skip-gated (HTTP-unreachable, brands.ts bounty==0x0).

31-vote: the vote shells are retiring -> work.<org>.network. Arm a hanzo.vote
shell-health guard that hammers cold loads and fails on the intermittent blank-#root
white-screen (Buffer-crash class) OR the "Made with love by Lux" cross-brand leak —
a real, known-flagged regression, kept armed (not muted/skipped). lux/zoo/pars green.

work-boards: 1 -> 10 passing (3 false failures fixed, Pars un-skipped, 2 skip).
vote: hanzo.vote deterministically flagged; lux/zoo/pars (6) green, 4 skip.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00
zeekayandHanzo Dev 1e40737310 test(e2e): dao onchain / vote / work / bounty / pars coverage
Regression-test the full DAO/governance/work-market surface across the live
orgs, read-only (no txs/writes/signing), asserting REAL functionality:

- 30-dao-onchain: BountyV1/ReputationV1/Governor/Safe on Zoo 200200 + Pars
  494949 via eth_call/eth_getCode — code present, bountyCount>=1, stateOf(0)==
  Paid(6), treasury()==org DAO Safe, ReputationV1.writer()==BountyV1 (wiring).
  Lux 96369: canonical CREATE2 Safe suite present, dead Governor asserted 0-byte
  (staged post-flag-day). Hanzo 36963 skip-gated until deploy lands.
- 31-vote-surfaces: lux/zoo/hanzo/pars.vote render + correct brand + ZERO cross-
  brand/competitor leakage + no white-screen (#root descendants + body text) +
  Connect Wallet present. reads-chain gated skip-until-live (Decent-fork shells).
- 32-work-boards: work.zoo.network LIVE badge (green, not FIXTURE), bounty in
  Paid column, on-chain ReputationV1 block, UI bounty count == chain bountyCount.
  work.{lux,hanzo,pars} skip-until-live.
- 33-bounty-bot: bounties.zoo.network webhook routed+POST-only (405), unsigned
  POST rejected (401), healthz internal-scoped (404 public correct). No secrets.
- 34-pars-chain: Pars 494949 chainId + monotonic height + explorer renders.

helpers.ts: additive ethCall/ethGetCode/hasCode/decodeAddress/encodeUint +
HANZO/PARS API domains + orgRpc() (one way to resolve any org's C-Chain RPC).
17-governance: drop dead unused SELECTORS block (on-chain contract asserts now
own their home in 30-dao-onchain). package.json: wire specs into test:full +
per-spec scripts.

36 active tests pass live, 8 skip-gated (ready to flip). Suite stays GREEN.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00
zeekayandHanzo Dev 6842224772 test(e2e): dao onchain / vote / work / bounty / pars coverage
Regression-test the full DAO/governance/work-market surface across the live
orgs, read-only (no txs/writes/signing), asserting REAL functionality:

- 30-dao-onchain: BountyV1/ReputationV1/Governor/Safe on Zoo 200200 + Pars
  494949 via eth_call/eth_getCode — code present, bountyCount>=1, stateOf(0)==
  Paid(6), treasury()==org DAO Safe, ReputationV1.writer()==BountyV1 (wiring).
  Lux 96369: canonical CREATE2 Safe suite present, dead Governor asserted 0-byte
  (staged post-flag-day). Hanzo 36963 skip-gated until deploy lands.
- 31-vote-surfaces: lux/zoo/hanzo/pars.vote render + correct brand + ZERO cross-
  brand/competitor leakage + no white-screen (#root descendants + body text) +
  Connect Wallet present. reads-chain gated skip-until-live (Decent-fork shells).
- 32-work-boards: work.zoo.network LIVE badge (green, not FIXTURE), bounty in
  Paid column, on-chain ReputationV1 block, UI bounty count == chain bountyCount.
  work.{lux,hanzo,pars} skip-until-live.
- 33-bounty-bot: bounties.zoo.network webhook routed+POST-only (405), unsigned
  POST rejected (401), healthz internal-scoped (404 public correct). No secrets.
- 34-pars-chain: Pars 494949 chainId + monotonic height + explorer renders.

helpers.ts: additive ethCall/ethGetCode/hasCode/decodeAddress/encodeUint +
HANZO/PARS API domains + orgRpc() (one way to resolve any org's C-Chain RPC).
17-governance: drop dead unused SELECTORS block (on-chain contract asserts now
own their home in 30-dao-onchain). package.json: wire specs into test:full +
per-spec scripts.

36 active tests pass live, 8 skip-gated (ready to flip). Suite stays GREEN.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:03 -07:00