Pin the lux-mainnet and hanzo-mainnet deployments to the CI images built from
23a5a89 (FIXTURE gate + title wrap + per-brand favicon). Applied + rolled out
to do-sfo3-lux-k8s / do-sfo3-hanzo-k8s; verified live 4/4 breakpoints.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Three prod UI fixes from the 4-res audit (work.lux.network + work.hanzo.network):
1. FIXTURE debug bar in prod. The LIVE/FIXTURE data-source pill (and the raw
RPC error string it surfaces, e.g. "Missing or invalid parameters") was
pinned bottom-left for every visitor and clipped past the mobile viewport.
Gate it to `vite dev` (import.meta.env.DEV) or an explicit VITE_SHOW_STATUS
flag, and cap the pill at calc(100vw-1.5rem) + overflow-hidden so it can
never overflow even in dev.
2. Bounty-card title truncation. Space-card names ("Ecosystem &
Partnerships", "Community & Social", "Content Creation") were ellipsis-
clipped at tablet/laptop. Drop `truncate`; let the title wrap.
3. Favicon. Replace the generic emerald green-check placeholder with each
brand's own square mark, injected per-build by the brand-html Vite plugin
as an inline data: URI (CSP-safe, no extra request). New purpose-built
16px-legible favicon.svg per brand (lux/hanzo/zoo/pars).
Board still reads $0 / 0 tasks because the work-market contracts are not yet
deployed on 96369/36963 (owner-gated) — untouched here.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Vite 8's Rolldown bundler needs @rolldown/binding-linux-x64-musl, but a committed
darwin-generated lock omits it and npm bug #4828 won't add it on install → 'Cannot find
native binding'. Copy only package.json and npm install fresh on the linux-musl build host so
the correct platform bindings resolve. Repo lock stays for local-dev reproducibility.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
The @luxfi/ui migration bumped to TypeScript 7 (native) + Vite 8 (Rollup 4), both of which
ship platform-specific PREBUILT binaries as optionalDependencies. Two failures resulted in the
alpine CI build: (1) npm ci against a darwin-generated lock lacked the linux-musl variants, and
(2) the TS7 native compiler has no alpine/musl binary at all, so 'tsc --noEmit' can't run there.
Fix both the ecosystem-standard way (same as the dao-vote image): npm install resolves the
platform-correct optional deps on the linux build host, and the image builds the bundle with
vite directly (esbuild transpiles TS; the typecheck is a dev/CI-lint concern, verified locally).
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
The @luxfi/ui@7.4.0 migration regenerated the lock with npm 11, which dedupes the
typescript@6.0.3 that @luxfi/ui's @expo/* deps require — but the Dockerfile builds on
node:22-alpine (npm 10) via 'npm ci', which demands those nested entries and failed
EUSAGE 'Missing: typescript@6.0.3 from lock file'. Regenerate the lock with npm 10 so it
matches the build tool; no bypass of npm ci.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
The Connect sheet's wallet method was Phantom-specific — window.phantom.ethereum only —
so eth_requestAccounts never fired for MetaMask/Rabby/Coinbase users (Zoo/Pars Connect
read as a no-op). Replace the narrow 'phantom' WalletKind with a generic 'injected' one
that resolves window.ethereum (any injected EIP-1193 wallet, Phantom included) and opens
the wallet's own connect prompt, so claim->sign is reachable on every brand.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Phase-1 built @luxfi/ui locally and consumed it via `file:`, which blocked the
merge. 7.4.0 is now published to npm WITH dist/ (real tarball, 192 dist entries)
via the KMS-gated release path, so switch the dep `file:` -> `@luxfi/ui@7.4.0`
(exact, matching the sibling @hanzogui pins). package-lock now resolves the
registry tarball (integrity sha512-LfUYgZFP…).
Drop the local `body .font_body { line-height: 1.5 }` pin from src/index.css:
7.4.0's AppProvider now ships that Tailwind-Preflight reset itself (injected once
via useInsertionEffect), so every consumer — not just the work-board — inherits
it. One place, one way.
Verified against the npm package: build green (tsc + vite 8.1.5, 1622 modules);
visual-regression 25/25 goldens within 2% (21 zoo + 4 pars white-label), incl.
the task-detail sheet + ⌘K palette overlays that the pin originally fixed.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
@hanzo/gui's GuiProvider wraps the app in a Tamagui `font_body` context that
pins an absolute line-height (23px). That cascaded into the work-board's
arbitrary-size text (text-[11px] field labels etc., which carry no line-height
utility), making each ~6.5px too tall and reflowing the bottom-anchored
task-detail sheet by ~33px on mobile. Restore the Tailwind-Preflight baseline
(html line-height:1.5) the work-board was authored against.
Visual-regression now 25/25 work-board goldens within 2% (21 zoo + 4 pars
white-label). task-detail modal panel height matches the pre-migration build
exactly (576px == 576px).
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Set the Zoo (200200) and Pars (494949) addresses (bounty/escrow/reputation + karma,
+ Pars governor) to the newly deployed DAO-Safe-owned work-market, superseding the
smoke set the boards read today. Owner/governor unchanged. Lux + Hanzo untouched.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
All four white-labels pinned to the single green sha 6fcf7a2 (identical source to
477a05f — only the CI matrix changed):
zoo sha-6fcf7a2 work.zoo.network (zoo-mainnet / zoo-k8s, @file)
pars pars-6fcf7a2 work.pars.network (pars / pars-k8s, @file)
lux lux-6fcf7a2 work.lux.network (lux-mainnet / lux-k8s, IngressRoute)
hanzo hanzo-6fcf7a2 work.hanzo.network (hanzo-mainnet / hanzo-k8s, @file)
lux-k8s's public ingress reads the kubernetescrd provider (Traefik IngressRoute,
group hanzo.ai/v1alpha1), not @file, so work.lux.network mirrors api.lux.network
(certResolver letsencrypt, Cloudflare-proxied). hanzo mirrors api.hanzo.network
(@file router, certResolver letsencrypt via DNS-01, grey). One brand per image;
zero cross-brand leak.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Mirrors the zoo/pars legs — VITE_BRAND selects the src/brands.ts profile and
VITE_RPC_URL bakes the brand's live CORS-enabled RPC into the bundle. Two more
immutable per-commit images from identical source:
lux -> ghcr.io/luxfi/work-board:lux-<sha> (work.lux.network, #7000FF)
hanzo -> ghcr.io/luxfi/work-board:hanzo-<sha> (work.hanzo.network, #ea580c)
One brand per build; zero cross-brand leak. amd64-only static SPA, luxfi ARC pool.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
- brands.ts was importing .svg logos at top level; vite.config.ts imports
brands.ts (for build-time title/CSP), and the config bundler parses SVG
markup as JSX → 'Unexpected JSX expression'. Split the .svg imports into a
runtime-only src/brand-logos.ts (BRAND_LOGOS keyed by BrandKey); brands.ts
is now pure, bundler-safe config data. ui.tsx BrandMark/BrandLogo read
BRAND_LOGOS[BRAND_KEY].
- fixture.ts: add lux+hanzo empty fixtures (no live bounty yet) to satisfy
Record<BrandKey,...> after brands.ts grew to 4 brands.
Full build green (tsc + vite). Lands the in-flight ⌘K palette, 6-category
symmetrical grid, real brand logos, and proper accents (Zoo #6c5efb indigo,
Pars #1C3879 Persian blue, Lux #7000FF, Hanzo #ea580c — placeholder green gone).
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Builder + brand agents paused on the weekly usage cap (resets Jul 21 6am PT).
Preserving in-flight work-board work so nothing is lost; next resume greens + finishes.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Render brand.spaces as a 1→2→3 column grid (1-col phone, 2×N tablet,
3×2 desktop for six categories) — driven by whatever spaces the brand
supplies, no hardcoded count, so it stays symmetrical as ae530aa6
expands Zoo/Pars to six. Cards are peaq-style: emoji tile + name +
"N open tasks · N contributors" (singular/plural aware), uniform
structure so the grid reads as one clean block.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
One palette over bounties / spaces / views / quick actions (new
suggestion, connect). ⌘K / Ctrl-K toggles it on desktop (discoverable
via a Search pill in the topbar); a ≥44px search icon in the mobile top
bar opens the SAME palette as a full-screen bottom-sheet. Keyboard-nav
↑/↓ + Enter, Esc/⌘K to close; the rendered order and the selection
index share ONE grouped list so the highlighted row is always the row
Enter runs. Dependency-light (no kbar) — a subsequence fuzzy scorer.
App also composes the prior 64px icon-rail split into the shell.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
min-w-0 on the leaderboard grid cells lets long addresses/amounts
truncate instead of forcing page-level horizontal scroll at 390px;
narrower snap widths keep board columns fully on-screen on phones.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Mobile-first (390px up, enhance to desktop):
- Sidebar → off-canvas drawer behind a hamburger (overlay + tap-close + safe-area);
static 256px sidebar only at md+. Mobile top bar carries hamburger + workspace + Connect.
- Kanban → horizontal scroll-snap columns (~86vw, peek next) on mobile; 4-col grid at xl.
- Leaderboard / All-Contributors tables wrapped in overflow-x scroll regions (min-w),
body never scrolls sideways. Tab row is horizontally scrollable.
- Task-detail + suggestion modals are full-bleed / bottom-sheet on mobile, centered cards at sm+.
- Overview stacks main + right rail on mobile; all view paddings responsive.
- index.css: overflow-x hidden, env(safe-area-inset-*), clamp() fluid type, thin scroll-x utility.
Design tokens: consolidated every surface hex to the Dework palette as CSS vars
(--bg #0e0e10 / --rail #141416 / --surface #17171b / --border #232328 / --hover #1e1e24),
set per-brand --brand accent at runtime. Deleted the now-unused theme.ts (dead code).
Leaderboards: Task Points = global Karma (formatAmount(karma)) with a transparent
tasks×100 proxy until Karma is canonical on-chain; dropped the separate Karma column.
Contract naming: ABI imports stay indirected via abi.ts (one-line swap for the
finished non-versioned Bounty/Escrow/Reputation/Karma set); scrubbed V1/V2 from
comments + README; README now documents the full platform.
Every view still renders from the fixture with no wallet/chain (screenshot-able).
Verified live at work.zoo.network (real Zoo data) + local at 390px and 1440px.
package: 0.2.0 -> 0.3.0
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Extends the on-chain work-board into a full Dework-style community surface,
white-labeled per domain, on our own primitives. Every view derives from ONE
on-chain read (loadWorkspace) so header/board/leaderboard numbers can't drift.
Views (all reading live Bounty/Reputation over JSON-RPC):
- Overview: org header + Time-to-payment / Total-paid stats (from PaymentReleased
+ BountyProposed event timestamps), Discord role-gate banner, Bounties category
grid (Spaces), Projects, right rail (About/Contributors/Admins).
- Board: 4-lane kanban (To Do/In Progress/In Review/Done) with Dework empty-state
framing; task cards show title + skill tags + reward + open-to badge + assignee.
- Open Tasks: rows grouped by status with skill tags + reward + assignee.
- Leaderboards: Top Contributors + Top Reviewers (7d/30d/all-time; windows from the
event stream, all-time from Reputation) + All Contributors; global-Karma column.
- Community Suggestions: create modal + upvotes (local-first, IAM/on-chain-swappable).
- Combined Board (all Spaces) + Space tabs (Overview/Board/Open Tasks/Suggestions).
- Task detail: on-chain description + real activity log (created→funded→claimed→
submitted→accepted→paid) + apply/comment flow + status/assignee/reviewers.
- Explore: cross-space skill-filtered index + sibling-DAO links.
Data layer:
- chain.loadWorkspace assembles bounties + activity + stats + contributors (workers
∪ reviewers) in one pass; block timestamps batched.
- Reward model is a native/ERC-20/ERC-721/ERC-1155 union — parseReward reads
rewardType+tokenId when the finished Bounty struct carries them (NFT rewards
render with tokenURI metadata), defaults to native/ERC-20 today.
- Karma optional per brand (global soul-bound rep); degrades cleanly when absent.
Config-driven white-label: Spaces/skills/tagline/accent/socials/Karma/IAM added to
the existing VITE_BRAND brand map (zoo, pars); one canonical config, one image/brand.
Hash router (no dep); Dework dark palette; per-brand accent via --brand.
package: 0.1.0 -> 0.2.0
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
One repo, one Dockerfile. Brand-varying values (chain, addresses, owner
label, header/title, CSP RPC host) move into src/brands.ts, selected at
build time by VITE_BRAND (default zoo). The zoo profile reproduces the
prior hardcoded values exactly, so work.zoo.network is unchanged.
Adds the pars profile (chain 494949, BountyV1 0x316B..cf1F) plus CI and
k8s to stand up work.pars.network:
- docker.yml: a brand matrix builds zoo :sha-<sha> and pars :pars-<sha>
from the same Dockerfile on the luxfi ARC pool.
- k8s/pars/: Deployment + Service (ns pars) + Traefik route fragment.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This cluster's hanzoai/ingress (Traefik v3) routes every public zoo host through
the zoo-dynamic ConfigMap (@file provider) with TLS auto-issued by its built-in
ACME resolver over Cloudflare DNS-01 — exactly how api.zoo.network is served.
Kubernetes Ingress objects don't produce the :443 router here (the api-zoo-network
Ingress is vestigial too), so replace k8s/ingress.yaml with k8s/route.yaml, the
router+service fragment merged into zoo-system/zoo-dynamic. One ACME client
(Traefik), no cert-manager. Verified: https://work.zoo.network serves 200 with a
valid LE cert (CN=work.zoo.network).
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
The immutable image built by CI for commit 5331a19, verified present in GHCR
and rolled out 2/2 on zoo-k8s (namespace zoo-mainnet).
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
The hanzoai/.github reusable is a private cross-org repo and both orgs are on
the free plan (no shared enterprise), so GitHub blocks the `uses:` at parse
time — every luxfi caller of it fails at 0s. Switch to the in-org self-hosted
build pattern proven green by luxfi/node/dex/genesis: runs-on lux-build-amd64,
GHCR login via GITHUB_TOKEN, metadata-action → immutable ghcr.io/luxfi/work-board:sha-<sha7>,
build-arg VITE_RPC_URL baked to the live Zoo RPC. amd64-only (deploy target is amd64).
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
On-chain bounty kanban (Vite+React+viem static SPA) that reads the live
BountyV1/EscrowV1/ReputationV1 work-market on Zoo (chain 200200) directly
over the CORS-enabled JSON-RPC. Split out of luxfi/dao into its own repo so
CI builds a clean image.
- .github/workflows/docker.yml: canonical hanzoai/.github reusable build,
bakes VITE_RPC_URL=https://api.zoo.network/v1/bc/C/rpc, pushes
ghcr.io/luxfi/work-board:sha-<commit> on self-hosted arcd runners.
- k8s/: Deployment (2 replicas) + Service (80->3000) + Ingress
(hanzoai/ingress class, host work.zoo.network, letsencrypt-prod).
Co-authored-by: Hanzo Dev <dev@hanzo.ai>