31 Commits
Author SHA1 Message Date
zeekayandHanzo Dev a5460ed12e Say which method each registration answers
Hanzo CI/CD / cicd (push) Failing after 6m22s
CI/CD / cicd (push) Failing after 6m23s
A registration that is not a typed operation is spelled Raw now, with the method
named, so opting out of the typed model reads as what it is.

The zip this pins also states the wire explicitly, which matters more than the
version gap suggests: JSON semantics follow the toolchain, so the older pin
answered with different bodies than the code was written against once the
builder moved.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 04:26:51 -07:00
zeekayandHanzo Dev cc7a80f1cd deps: pin go.temporal.io/server v1.32.0-157.0 (fork base); finish temporal-fork rip; ui/dist embed placeholder
Co-authored-by: Hanzo Dev <[email protected]>
2026-06-30 16:10:51 -07:00
zeekay 1e76fabaff chore: gitignore build artifacts + drop 80 tracked files
dist/.next/target/etc. should never have been tracked.
Regenerated by build pipeline.
2026-06-01 15:19:36 -07:00
zeekay 45bd80a26d feat(tasks): v3.9.0 — distributed engine: per-NS SQLite shards + PQ consensus + sticky routing
Architecture
  One SQLite shard per (org, namespace) under <DataDir>/<org>/<ns>.db
  with WAL mode, single writer, 5s busy timeout. Replaces the prior
  shared memdb/zapdb store. Shard manager opens/caches/idle-evicts;
  prefix scans, namespace isolation, and cross-restart durability all
  proven by tests.

Replication (post-quantum, native)
  New pkg/tasks/replication/ exposes a Replicator interface with two
  drivers:
    local  — single-node passthrough (default for embedded/dev)
    quasar — wraps github.com/luxfi/consensus NewPQ (BLS + Corona
             dual-threshold). On every WAL-affecting op the shard
             builds a Frame, calls Propose, awaits Decision, applies
             on Accept. Followers receive accepted frames via the
             same Subscribe path so every node converges.

Sticky task-queue routing
  New pkg/tasks/routing/ — rendezvous-style smallest-hash leader
  election keyed by SHA-256(shard|taskQueue|seed). OnChange fan-out
  re-hashes on cluster membership change. Dispatcher consults Router
  before accepting subscriptions and forwards non-leader requests.

Namespace migration
  New pkg/tasks/migration/ — lock → checkpoint → copy → unlock with
  a job lifecycle (pending|locking|copying|replaying|released|failed).
  CLI: `tasksd migrate --org <id> --namespace <name> --to <node>`.
  HTTP: POST /v1/tasks/namespaces/{ns}/migrate.

New env / flags
  --data / TASKSD_DATA_DIR        shard root (./tasks-data)
  --replicator / TASKSD_REPLICATOR local | quasar (default local)
  --node-id / TASKSD_NODE_ID      stable id (required for quasar)
  --validators / TASKSD_VALIDATORS CSV host:port
  --validator-key / TASKSD_VALIDATOR_KEY (reserved for PQ key path)
  TASKSD_STORE                    sqlite | memory (default sqlite)

New routes
  GET  /v1/tasks/cluster
  GET  /v1/tasks/cluster/health
  POST /v1/tasks/namespaces/{ns}/migrate

UI bundle (synced)
  Cluster page with status / validators / shards / leader-map.
  NamespaceDetail "Migrate to node" button gated by
  canWriteNamespace + cluster.enabled. MigrationDialog enforces
  typed-name confirmation. Sidebar Cluster nav hidden when running
  single-node.

Tests
  +33 backend tests (store: 11, replication: 10, routing: 5,
  migration: 3, cluster routes: 2, store factory: 2). All packages
  PASS with -race; go vet clean.
  +13 UI tests (effectiveHealth: 6, canSubmitMigration: 4,
  useCluster: 3). 169 total UI tests, 1 pre-existing skip.

Migration v3.8.x → v3.9.0
  Fresh-data startup. zapdb backend dropped. Operators drain old
  cluster, snapshot for archival, deploy v3.9.0 with new --data
  directory. To enable HA, scale to 3+ replicas with --replicator
  quasar and matching --node-id / --validators.
2026-04-30 00:09:39 -07:00
zeekay a3cbc7c7f5 fix(tasks): sync admin-tasks bundle for Deployment type alignment
Bundles the wire-shape fix (Deployment: name + versions). Front-end was
reading seriesName/buildIds from the v3.7.x type which v3.8.0 renamed.
1 Deployment list page rendered blank with TypeError; now renders
correctly.
2026-04-29 20:38:37 -07:00
zeekay 18c2c5cf0b feat(tasks): v3.8.0 — full parity wave (activities, deployment CRUD, validate)
Backend additions
  Standalone activities — first-class records keyed by (ns, activityId,
  runId). Engine: StartActivity, ListActivities, DescribeActivity,
  CancelActivity, CompleteActivity, FailActivity, HeartbeatActivity
  (FSM-rejecting + auto SCHEDULED→STARTED), GetActivityHistory.
  Heartbeat tolerated only on running. Idempotent on requestId.

  Deployments — full CRUD. CreateDeployment(ns,name,desc,owner,compute)
  rejects dup. UpdateDeployment patches description/owner/compute.
  DeleteDeployment refuses if versions exist unless force. CreateVersion
  promotes first version to current; rejects dup buildId. UpdateVersion
  metadata-only. ValidateVersion returns synthetic snapshot pending
  worker SDK runtime.

New HTTP routes (15)
  GET    /v1/tasks/namespaces/{ns}/activities
  POST   /v1/tasks/namespaces/{ns}/activities
  GET    /v1/tasks/namespaces/{ns}/activities/{id}/{run}
  POST   .../{id}/{run}/{cancel,complete,fail,heartbeat}
  GET    .../{id}/{run}/history
  POST   /v1/tasks/namespaces/{ns}/deployments  (create, 409 on dup)
  GET    /v1/tasks/namespaces/{ns}/deployments/{name}
  POST   /v1/tasks/namespaces/{ns}/deployments/{name}  (patch)
  DELETE /v1/tasks/namespaces/{ns}/deployments/{name}?force=
  POST   /v1/tasks/namespaces/{ns}/deployments/{name}/versions
  POST   .../versions/{buildId}  (patch metadata)
  POST   .../versions/{buildId}/validate

Tests
  +24 top-level test functions across activities and deployments.
  go test ./... -race -count=1 PASS, go vet clean.

UI bundle (synced)
  Activities feature — list/start/detail with 5 tabs (summary, history,
  workers, search-attrs, user-metadata). Action menu FSM-gated; capability-
  gated. Activity history reuses HistoryTree.

  Worker-deployment CRUD — Create/Edit/Delete deployments, version
  Create/Edit/Delete, validate-connection modal.

  Workflows-list polish — bulk-select toolbar (Cancel/Terminate/Reset/
  Signal across selected, builds Batches.create with WorkflowId IN(...)),
  saved-view persistence in localStorage keyed by (org, namespace),
  filter chips (status, time range, search-attribute) round-tripped
  through parseSearchQuery.

  Polish — ConfigurableTableHeadersDrawer + useTableColumns hook;
  TimelinePane (Gantt over activities/timers/child-workflows);
  PendingNexusPane; PayloadInputWithEncoding + PayloadDecoder +
  RetryPolicyInput reusable widgets.

UI tests: 156 passed, 1 skipped, 0 failed (was 115; +41 new).
2026-04-29 20:31:55 -07:00
zeekay 1444d61c56 feat(tasks): v3.7.0 — multi-tenant hardening, durable store, full inverse-options, 13 new endpoints
Security (red-team pass)
  - ZAP wire handlers were org-blind: every opcode bypassed WithOrg
    and used the unscoped engine. Patched: zapHandlers takes a
    JWTValidator; per-request engine is scoped to claims.Owner.
    TASKSD_REQUIRE_IDENTITY enforces validation in production.
  - /v1/tasks/mcp tools/call ran on the unscoped engine — patched
    to use auth.OrgID(r.Context()).
  - /v1/tasks/events SSE fan-out was global; events now stamp OrgID
    via engine.emit(ev) and the SSE handler filters per-request org.
  - AddSearchAttribute rejected reserved/system names (WorkflowId,
    ExecutionStatus, TaskQueue, *StartTime, Temporal*).
  - Path-traversal guards on every {ns}/{id} URL slot (validIdent +
    validPathSegment).
  - dispatcher.queries map is now bounded (4096 entries, 5m TTL,
    drop-oldest on overflow) — closes a DoS via silent worker.

Durability
  - newStoreFromEnv selects memdb (default) or zapdb via
    TASKSD_STORE=zapdb. Survives restart; covered by
    TestStore_ZapdbPersistsAcrossOpen.

Inverse-of-options
  - RevokeIdentity, DeleteNexusEndpoint, RemoveSearchAttribute,
    DeprecateNamespace, DeleteDeploymentVersion, UnsetCurrent.
  - Schedule full update + trigger + matching-times.

13 new HTTP routes
  GET    /v1/tasks/settings
  DELETE /v1/tasks/namespaces/{ns}
  POST   /v1/tasks/namespaces/{ns}/schedules/{id}            (full update)
  POST   /v1/tasks/namespaces/{ns}/schedules/{id}/trigger
  GET    /v1/tasks/namespaces/{ns}/schedules/{id}/matching-times
  POST   /v1/tasks/namespaces/{ns}/deployments/{name}/set-current
  DELETE /v1/tasks/namespaces/{ns}/deployments/{name}/versions/{buildId}
  GET    /v1/tasks/namespaces/{ns}/archival
  GET    /v1/tasks/namespaces/{ns}/workflows/{id}/executions
  GET    /v1/tasks/namespaces/{ns}/task-queues/{q}/partitions
  GET    /v1/tasks/namespaces/{ns}/workers/{identity}
  DELETE /v1/tasks/namespaces/{ns}/nexus/{id}
  DELETE /v1/tasks/namespaces/{ns}/identities/{email}
  DELETE /v1/tasks/namespaces/{ns}/search-attributes/{name}

Tests
  go test ./pkg/tasks/... -race -count=1 → PASS, +22 new tests
  (cross-org keyspace isolation, query non-widening, reserved attrs,
  pendingQuery bounds, TTL eviction, Identifier validation, persistence
  cross-restart). go vet clean.

UI bundle (synced)
  Canonical Hanzo H mark in HanzoMark.tsx replaces stylized H rectangle.
  useSettings() hook gates write actions when namespaceWriteDisabled,
  archival/heartbeats/advanced-visibility flags. New inverse buttons
  (worker stop, identity revoke, search-attr delete, deployment unset,
  batch terminate confirm).
2026-04-29 18:11:13 -07:00
zeekay 313cb10002 fix(tasks): top-level /v1/tasks/nexus aggregates endpoints across namespaces
UI's cross-namespace Nexus list page (no ns selector) hit /v1/tasks/nexus
and got 404. Backend now serves a top-level GET that scans the nx/ key
prefix across namespaces. Read-only — namespace-scoped routes remain the
only mutation surface.

Bundles UI fix for EventDetail (handles flat events response + string
eventId param).
2026-04-29 14:48:22 -07:00
zeekay 0148e21bd6 feat(engine): cancel handshake, ZAP query push, full visibility grammar, admin endpoints
Closes the remaining 501 surfaces and routes worker-bound RPCs through
the existing ZAP dispatcher (no polling).

Cancel handshake
  Running → CANCELING via OpcodeDeliverCancelRequest (0x00B3) push to
  the workflow's task-queue subscribers. Worker acks via existing
  RespondWorkflowTaskCompleted with a Canceled command (Kind=3) →
  CANCELED. 60s sweep auto-acks if no subscriber.

Query push
  Allocates a query token, picks a subscriber via the existing
  round-robin dispatcher, sends OpcodeDeliverQuery (0x00B4),
  blocks on a per-token channel with ctx timeout.
  Worker replies via OpcodeRespondQuery (0x00C4).
  HTTP returns 503 when no workers are subscribed, 504 on timeout.

Visibility grammar
  Replaces the AND-only parser with full grammar:
  OR / NOT / parens / IN / NOT IN / BETWEEN. Existing AND-only
  callers round-trip unchanged.

Admin endpoints (no longer 501)
  POST /v1/tasks/namespaces/{ns}/search-attributes (+ GET list)
  POST /v1/tasks/namespaces/{ns}/metadata
  POST /v1/tasks/namespaces/{ns}/batches/{id}/terminate
  POST /v1/tasks/namespaces/{ns}/workflows/{id}/metadata

UI bundle
  Embedded SPA refreshed to the React 19 + hanzogui port with
  route-level code-splitting (main entry 67KB / 14KB gz).

Tests
  go test ./pkg/tasks/... -race -count=1 → 36 tests, 0 failures.
2026-04-29 14:37:13 -07:00
zeekay 51f359f630 chore: purge tamagui — use @hanzo/gui only natively
- Makefile, ui/README.md, ui/embed.go: comments Tamagui → @hanzo/gui
2026-04-28 08:45:07 -07:00
zeekay d03bb8370a ops(scripts): sync-admin-ui.sh points at gui/apps/admin-tasks (was code/admin-tasks) 2026-04-27 14:33:28 -07:00
zeekay 9ad481b49b ui: sync v2.49.0 admin-tasks bundle (rename @hanzogui/admin → @hanzo/admin, track upstream temporal/ui semver) 2026-04-27 09:57:33 -07:00
zeekay 827619846d ui: sync v0.3 admin-tasks bundle (top 5 parity gaps closed)
Bundle synced from ~/work/hanzo/gui/code/admin-tasks/dist/.
Closes 75% of upstream Temporal Web UI parity (was 55%):

  ✓ Per-tab counts on workflow detail
  ✓ SavedViewsRail on Workflows list (All/Task Failures/Running/
    Parent/Today/Last Hour)
  ✓ DataTable primitive — Schedules / Batches / Deployments now
    render headers + empty body row with copy (was empty cards)
  ✓ Input + Result panels on workflow detail Summary tab
  ✓ Event timeline strip with colored bars by event family

Bundle: 693 → 701 KB raw / 206 → 208 KB gzip (+8 KB raw / +2 KB
gzip for 5 features).

Live (curl http://127.0.0.1:7243):
  /_/tasks/                                       → 200
  /_/tasks/assets/index-BH7J2vN-.js               → 200, 700,904 bytes
  /_/tasks/assets/index-CL1JYgvC.css              → 200, 9,869 bytes
  /_/tasks/namespaces/default/{workflows,schedules,batches,deployments}
                                                  → all 200

Remaining gaps (next 3):
  - Workflow header status pill ("big") + WorkflowActions menu
    (terminate/cancel/signal/reset/pause)
  - Time-zone aware timestamps (upstream uses timestamp.svelte rune)
  - WorkflowDetails breadcrumbs strip above tabs
2026-04-27 09:12:40 -07:00
zeekay ff3c5e3557 ui: sync v0.2 admin-tasks bundle (all 11 workflow tabs + Activities + Archive)
Bundle synced from ~/work/hanzo/gui/code/admin-tasks/dist/. Adds 9
new workflow detail tab routes + namespace-level Activities + a real
Archive explanation page. Page count went 14 → 22 (out of upstream
Temporal Web UI's 45 — closes 70% of remaining parity gap).

Bundle: 675 KB → 693 KB raw / 199 KB → 206 KB gzip. +18 KB raw
(+5 KB gzip) for 9 new tab panes.

Verified live (curl http://127.0.0.1:7243):
  /_/tasks/                                                  → 200
  /_/tasks/assets/index-PlzglzSM.js                          → 200, 693 KB
  /_/tasks/assets/index-DxGu2tVq.css                         → 200, 9 KB
  /_/tasks/namespaces/default/workflows/test/call-stack       → 200
  /_/tasks/namespaces/default/workflows/test/pending-activities → 200
  /_/tasks/namespaces/default/activities                      → 200
  /_/tasks/archive                                            → 200

Engine-gated tabs (Pending Activities, Workers, User Metadata,
Nexus Links, Activities namespace route) render honest empty states
with hints about the worker SDK runtime. Lying via fake data is not
the move.
2026-04-27 08:23:51 -07:00
zeekay 1a46fdf123 feat(ui): v3.0.0 — Tamagui IS the production UI, kill v1 React/shadcn
The embedded UI in tasksd is now the @hanzo/gui Tamagui build from
~/work/hanzo/gui/code/admin-tasks/. v1 React/shadcn deleted.

What's still here:
  ui/embed.go        — Go //go:embed wrapper (handler unchanged)
  ui/dist/           — synced from admin-tasks build output
  ui/README.md       — explains the bundle is built externally
  scripts/sync-admin-ui.sh — copies admin-tasks/dist → ui/dist + stamp
  Makefile           — `ui` target now calls the sync script
  Dockerfile         — drops the node ui-build stage; expects pre-built
                       dist/ in the repo or built upstream via CI

What's deleted (62 files):
  ui/{src,public,node_modules}/         entire trees
  ui/{package.json, vite.config.ts, tailwind.config.js, postcss.config.js,
      tsconfig.json, index.html, PERF.md, .gitignore, pnpm-lock.yaml,
      vite.perf2.config.ts, tsconfig.tsbuildinfo}
  UI-TAMAGUI-MOVED.md (no longer accurate)

Live bundle (verified curl http://127.0.0.1:7243/):
  /_/tasks/                       → 200
  /_/tasks/assets/index-BKuZ1al1.js → 200, 674,748 bytes
  /_/tasks/assets/index-DTXHKKx4.css → 200, 9,104 bytes
  /v1/tasks/namespaces            → 200

Bundle: 339 KB → 675 KB raw / 104 KB → 199 KB gzip. Larger because
Tamagui ships react-native-web + Tamagui core + theme primitives
that shadcn doesn't. Trade: design-system consistency across all
Hanzo admin UIs (kms, commerce, console, etc.) vs 95 KB gzip.

CI note: .github/workflows/build.yml's docker build needs a pre-step
that checks out hanzo/gui and builds admin-tasks, OR commits ui/dist/
to this repo. Dockerfile fails fast with a clear message until then.

Tag v3.0.0 because this is a meaningful UI architecture change.
2026-04-27 08:15:19 -07:00
zeekay 56c04aceda move: ui-tamagui-spike → hanzo/gui workspace as @hanzogui/admin-tasks
Tamagui Tasks UI moved into the canonical location at
~/work/hanzo/gui/code/admin-tasks/ where @hanzogui/static can resolve
workspace deps. Build now extracts static CSS, bundle drops to
199 KB gzip (under the 200 KB target).

UI-TAMAGUI-MOVED.md — one-page pointer for anyone looking for the
old ui-tamagui-spike/ directory. Points at the new home.

Embedded production UI at ui/ (React + @hanzo/ui-shadcn) is unchanged
— it remains the binary form factor for tasksd. admin-tasks deploys
as a standalone web app at tasks.hanzo.ai when v2.1 ships (lucide
deep-import + React.lazy).
2026-04-27 03:06:17 -07:00
zeekay 7fbe17d1a3 docs(perf) + refactor(spike): consume @hanzogui/admin
ui/PERF.md — Scientist's bundle audit. The big number: lucide-icons-2
ships all 1,761 icons through the umbrella for the 32 we use. That
single shim saves 783 KB raw / 73 KB gzip and is a 1-hour patch.
Combined with React.lazy() on 13 of 14 routes, v2 Tamagui bundle
lands at 590 KB raw / 183 KB gzip — under the 200 KB target and
better than today's React/shadcn v1 production embed (104 KB gzip).

ui-tamagui-spike/ — refactored to consume @hanzogui/admin instead of
its own copies. Sidebar/TopBar/PageShell + Alert/Badge/CopyField/
Empty/HanzoMark + lib/format/useFetch/lib/events all moved out
(~963 LOC deduped). Spike now at ~250 LOC of app-specific code.

Spike doesn't ship as the embedded UI yet — Tamagui static extraction
needs to clear up (see task #24: workspace move OR vite-plugin
resolver fix). But the structural win is real: the next Hanzo admin
UI (commerce, kms, console) lands by composing @hanzogui/admin, not
by copy-pasting chrome.

Cross-monorepo dedup target: ~3,749 LOC of admin chrome lives across
tasks/ui (v1) + ui-tamagui-spike + iam/ui + console/web. 1,418 LOC
already in the new package; another ~1,800 LOC saves on next round
when StatusBadge / DataTable / DialogForm / FilterBar+SavedViewsRail
extract.
2026-04-27 02:50:17 -07:00
zeekay db929b8b76 fix(ui): restore React/shadcn UI as production; demote Tamagui to dev spike
v2.0.0/v2.0.1 shipped a blank Tamagui UI: TamaguiProvider throws
"Missing theme" because @hanzogui/static can't resolve @hanzogui/core
from its `.hanzogui/` temp dir while we live outside the hanzo/gui
workspace. Disabling the extractor avoids the build error but skips
the CSS layer Tamagui needs to populate the theme registry at runtime.

Restoring the proven-working React/shadcn UI as the embedded production
UI (was at ui-shadcn-deprecated/, renamed back to ui/). Tamagui port
moves to ui-tamagui-spike/ as a dev-only reference — runs at :5174
via `pnpm dev` but does not embed.

ui/                — production React + @hanzo/ui-shadcn primitives
ui-tamagui-spike/  — dev-only Tamagui port; embed when Tasks moves
                     into ~/work/hanzo/gui workspace OR when the
                     extractor's temp-dir resolution is fixed upstream

Honest tradeoff: ship reliability over v2 perfection. Tamagui v2 ships
when the workspace move OR @hanzogui/vite-plugin's resolver lands.
2026-04-27 02:32:12 -07:00
zeekay 47bb0c071e fix(ui): use canonical hanzogui defaultConfig — restore theme registry
v2.0.0 shipped a blank UI: TamaguiProvider threw "Missing theme."
synchronously on mount because the previous hanzogui.config.ts was
spreading defaultConfig.themes into a new object literal, which
breaks the static shape getThemeProxied() depends on at runtime.

Reverted to the canonical pattern from
~/work/hanzo/gui/code/starters/expo-router/hanzogui.config.ts:

  export const config = createHanzogui(defaultConfig)

Hanzo brand recoloring will land via CSS variable overrides in
src/index.css (a follow-up — the canonical theme renders correctly
out of the box). Build clean, page renders, all routes 200.

Bundle 1.51 MB raw / 297 KB gzipped (unchanged — fix is shape-only).
2026-04-27 02:28:49 -07:00
zeekay d61eb2481d feat: full Tamagui port — kill shadcn, ship @hanzo/gui native UI
The embedded UI is now 100% Tamagui via @hanzo/gui. Pure primitives,
no shadcn, no @radix-ui, no tailwind. The 14 pages from temporal/ui
are mirrored 1:1 in shape with Hanzo identity.

Layout:
- ui/                       — the new (and only) UI source. Tamagui.
- ui-shadcn-deprecated/     — old React+shadcn UI, retained read-only
                              as historical reference. NOT in any
                              build path. Delete in v1.43.

Pages (14):
  Namespaces (list)              — Active/Inactive badges + retention
  NamespaceDetail                — Overview/Identities tabs, Region/
                                    Retention/APS cards, Connect dropdown
                                    with ZAP+HTTP endpoints + copy buttons
  Workflows                      — header + filter + table + Start dialog
  WorkflowDetail                 — Summary | Call stack | History tabs
  WorkflowHistory                — synthetic event timeline
  EventDetail                    — JSON tree
  Schedules                      — list view
  Batches                        — list + Start dialog
  Deployments                    — card grid
  TaskQueues / TaskQueueDetail   — running/total counts + workflows
  Workers                        — empty state + worker SDK hint
  Nexus                          — list of cross-ns endpoints
  Support                        — 4 cards

Chrome:
  Sidebar — Hanzo H mark + 8 nav items + Archive/Import/Docs + footer
  TopBar  — namespace switcher, local-time, theme toggle, account chip
  Realtime — useTaskEvents hook → SSE → useFetch.mutate per page

Lib:
  hanzogui.config.ts — Hanzo dark palette
  vite.config.ts     — base /_/tasks/, proxy /v1/tasks/* → :7243,
                       react-native → react-native-web alias
  embed.go           — //go:embed all:dist + Handler() (mirrors prior shape)

Numbers:
  28 files, 3389 LOC src
  Bundle 1.51 MB raw / 287 KB gzipped (87 KB over goal — Tamagui surface
    + umbrella import. Deep imports + tree-shake fix in v1.43.)
  Binary 12 MB. Build clean. Embedded bundle live at
  http://127.0.0.1:7243/_/tasks/ — all routes 200.

Blockers worked around:
  - v102 prop typing rejects long-form names (minWidth → minW etc.)
  - No \$mono font token in @hanzogui/config@7 → inline fontFamily
  - themeInverse not on Button v102 → explicit bg/color overrides
  - animation prop missing on Dialog.Overlay → static dim
  - text=uppercase shorthand maps to textAlign not textTransform → as any

What's still missing for 1:1 parity with ~/work/temporal/ui (deferred):
  - SavedViewsRail + system views on Workflows page
  - EmptyWorkflowsHero (moon-and-sea SVG + sample SDK code blocks)
  - FilterBar JSON-mode toggle
  - Schedule create dialog (read-only matches v1)
  - Workflow input/result/memo viewers on WorkflowDetail
  - Compaction/event-paging on WorkflowHistory
  - /admin, /cluster, /event-history-import — upstream-only routes

No gRPC. No proxy. No upstream temporal.io. ZAP wire on :9999, SSE
realtime on /v1/tasks/events. All native.
2026-04-27 01:52:58 -07:00
zeekay f06fa6878b feat: full Temporal-UI parity port + Tamagui v2 spike
Two parallel deliverables.

(1) v1 React UI feature port — pages added, all wired through SSE realtime:
  - TaskQueues             — list of queues (derived server-side from
                              listed workflows; running/total counts)
  - TaskQueueDetail        — workflows in a queue, summary stats
  - WorkflowHistory        — synthetic event timeline (start +
                              signal counter + terminal); UI shows the
                              "synthetic: true" hint until per-event
                              durable history lands in v1.42
  - Workers                — empty-state stub until the worker SDK
                              runtime ships (honest, not fake)
  - EventDetail            — full event payload viewer with JSON tree
  - WorkflowDetail (rewrite) — Summary | Call stack | History tabs;
                              Call stack tab calls QueryWorkflow with
                              type "__stack_trace" and renders the
                              501 gracefully

  Sidebar gains Task Queues + Workers between Deployments and Nexus.
  Routes mounted in main.tsx through PageShell. Bundle 320 → 339 KB
  (+5.6%, +2.7 KB gzip).

(2) Server surface — pkg/tasks/embed.go:
  GET    /v1/tasks/namespaces/{ns}/task-queues
  GET    /v1/tasks/namespaces/{ns}/task-queues/{q}
  GET    /v1/tasks/namespaces/{ns}/task-queues/{q}/workers     -> []
  GET    /v1/tasks/namespaces/{ns}/workers                     -> []
  GET    /v1/tasks/namespaces/{ns}/workflows/{wf}/history       -> synthetic
  POST   /v1/tasks/namespaces/{ns}/workflows/{wf}/query         -> 501
  No new opcodes — these are HTTP-only conveniences derived from
  engine state. ZAP wire stays the canonical native transport.

  pkg/tasks/taskqueues_test.go — both list + detail cases pass.

(3) v2 Tamagui UI spike at ui-tamagui/ — the hanzogui-native rewrite:
  - Plain Vite + Tamagui (gui-workspace starters use workspace:* deps,
    unusable standalone — pragmatic over heroic)
  - Single working Workflows page rendering live data via @hanzo/gui
    primitives (XStack/YStack/Card/Button/Text)
  - SSE realtime hook + 30-line useFetch in place of SWR
  - Bundle 446 KB JS / 139 KB gzip (+23% over v1; static extractor
    disabled because hanzogui workspace deps don't resolve outside
    the gui repo — revisit when we move ui-tamagui into the workspace)
  - README documents what's missing before v2 can replace v1
  - 5-day estimate to port the remaining 8 pages + chrome layer

go test ./pkg/sdk/... → 11/11 PASS over native ZAP.
Binary 7.66 MB. No gRPC, no proxy, no upstream temporal.io.
2026-04-27 00:50:58 -07:00
zeekay 156772e041 feat: realtime in browser + native Go subscriber
ui/src/lib/events.ts — useRealtime() React hook subscribes to
/v1/tasks/events via EventSource and invalidates SWR caches when
workflow / schedule / batch / namespace transitions affect the
current view. No polling. No proxy. Native SSE only.

Wired into:
  Workflows page  — live workflow.{started,canceled,terminated,signaled}
  Schedules page  — live schedule.{created,paused,resumed,deleted}
  Batches page    — live batch.started
  Namespaces page — live namespace.registered

pkg/sdk/client/events.go — Go callers get the same stream natively
via client.SubscribeEvents(ctx, httpAddr) → <-chan Event. No SDK
spin-up, no JSON-RPC overhead — long-lived TCP read with inline SSE
parsing. End-to-end verified with a /tmp demo: two POST workflow
starts hit the channel in real time as workflow.started events.

README.md — drop the stale gRPC compat reference. The wire is ZAP
plus an HTTP/JSON shim plus SSE + MCP for browsers and agents. No
gRPC, no go.temporal.io, anywhere.

Bundle 320.7 KB (+0.6 from 320.1). Binary 7.6 MB unchanged.
4/4 integration tests still pass.
2026-04-26 23:56:27 -07:00
zeekay b3627a14ca feat(ui): visual parity with Temporal Web UI
Port the Temporal Web UI structure (temporalio/ui SvelteKit reference at
/tmp/temporal-ui-ref/ui) to our React + @hanzo/ui shell. Match the
upstream layout 1:1 in shape; visual identity stays Hanzo (Hanzo H mark,
"Self-Hosted" sub-label, our @hanzo/ui tokens).

Sidebar:
- Hanzo H + "Hanzo Tasks / Self-Hosted" header
- Top group: Namespaces, Workflows, Schedules, Batch, Deployments, Nexus
- Divider
- Bottom group: Archive (disabled), Import (disabled), Docs (external)
- Footer: Heart Feedback link + version (v2.45.3 from VITE_APP_VERSION)

Top bar:
- NamespaceDropdown (shows current ns + external link to docs)
- LocalTimeIndicator (toggle local / UTC, persists in localStorage,
  exports formatTimestamp for tables)
- ArchiveToggle (eye icon, persists in localStorage)
- ThemeToggle (sun/moon)
- AccountChip

Workflows page:
- Big "{N} Workflows" header + RefreshCw + last-fetch timestamp +
  Start Workflow CTA
- FilterBar (filter pill + list/code mode toggle)
- Two-column body:
  - SavedViewsRail (All Workflows / Task Failures / Running /
    Parent Workflows / Today / Last Hour + Custom Views 0/20)
  - Right pane: table (Status / Workflow ID / Run ID / Type / Start / End)
    OR EmptyWorkflowsHero (moon-and-sea inline SVG + 6 sample SDK links
    pointing at github.com/temporalio/samples-{go,java,typescript,
    python,dotnet,php})

main.tsx routes Workflows full-bleed, every other page wraps in
PageShell for consistent padding.

Bundle: 319 KB JS (+16 from 303), 23.7 KB CSS. Binary 7.56 MB.
All 7 SPA routes serve 200, /v1/tasks/* surface unchanged.
2026-04-26 22:49:37 -07:00
zeekay 30e70000eb feat: native engine + @hanzo/ui shell (full Temporal Cloud parity)
Engine (pkg/tasks):
- store.go     — KV layer over luxfi/database memdb (snapshot-and-iterate
                 list to avoid RWMutex upgrade deadlock)
- types.go     — wire types: Namespace, WorkflowExecution, Schedule,
                 BatchOperation, Deployment, NexusEndpoint, Identity
- engine.go    — domain ops: register/list/describe ns; start/list/
                 describe/cancel/terminate/signal workflow; create/list/
                 describe/delete/pause/unpause schedule; start/list/
                 describe batch; create/list deployment; create/list
                 nexus endpoint; grant/list identity; cron sweeper goroutine
- embed.go     — every ZAP opcode (0x0060-0x0090) + every HTTP route
                 dispatches through the same model funcs; no drift

HTTP surface (browser-only JSON shim, ZAP is the canonical wire):
  GET    /v1/tasks/namespaces
  POST   /v1/tasks/namespaces
  GET    /v1/tasks/namespaces/{ns}
  GET    /v1/tasks/namespaces/{ns}/workflows
  POST   /v1/tasks/namespaces/{ns}/workflows
  GET    /v1/tasks/namespaces/{ns}/workflows/{id}
  POST   /v1/tasks/namespaces/{ns}/workflows/{id}/cancel
  POST   /v1/tasks/namespaces/{ns}/workflows/{id}/terminate
  POST   /v1/tasks/namespaces/{ns}/workflows/{id}/signal
  GET    /v1/tasks/namespaces/{ns}/schedules
  POST   /v1/tasks/namespaces/{ns}/schedules
  GET    /v1/tasks/namespaces/{ns}/schedules/{id}
  DELETE /v1/tasks/namespaces/{ns}/schedules/{id}
  POST   /v1/tasks/namespaces/{ns}/schedules/{id}/pause
  POST   /v1/tasks/namespaces/{ns}/schedules/{id}/unpause
  GET    /v1/tasks/namespaces/{ns}/batches
  POST   /v1/tasks/namespaces/{ns}/batches
  GET    /v1/tasks/namespaces/{ns}/batches/{id}
  GET    /v1/tasks/namespaces/{ns}/deployments
  POST   /v1/tasks/namespaces/{ns}/deployments
  GET    /v1/tasks/namespaces/{ns}/nexus
  POST   /v1/tasks/namespaces/{ns}/nexus
  GET    /v1/tasks/namespaces/{ns}/identities
  POST   /v1/tasks/namespaces/{ns}/identities

UI (full Temporal Cloud parity):
- @hanzo/ui primitives in src/components/ui/{button,card,tabs,badge,
  alert,dialog,input,label,skeleton,dropdown-menu}.tsx — same shadcn
  pattern @hanzo/ui itself uses; CSS tokens from @hanzo/ui design system
- App.tsx: sidebar layout (Workflows/Schedules/Batches/Deployments/
  Namespaces/Nexus/Support) with top bar (UTC clock, theme toggle,
  account chip, ns indicator, embedded badge)
- pages/Namespaces.tsx + pages/NamespaceDetail.tsx — list + detail w/
  Overview+Identities tabs, Region/Retention/APS cards, Connect
  dropdown showing ZAP+HTTP endpoints with copy buttons, Active badge
- pages/Workflows.tsx — list + Start Workflow dialog (real POST)
- pages/WorkflowDetail.tsx — execution details
- pages/Schedules.tsx — list w/ cron / interval describe
- pages/Batches.tsx — list + Start Batch dialog (operation enum)
- pages/Deployments.tsx — list of worker version series
- pages/Nexus.tsx — list of cross-ns endpoints
- pages/Support.tsx — 4 cards linking to docs / GitHub / health
- ThemeToggle (light/dark, persisted), UTCClock (live ticker),
  AccountChip (popover w/ identity + sign out)

Binary: 7.5 MB. Workflow execution semantics (deterministic replay of
user code) still pending — engine records state and tracks history len
on signals, but does NOT run user-supplied workflow goroutines yet.
Worker SDK runtime is the next phase.
2026-04-26 22:18:42 -07:00
zeekay 492ad649e8 infra: standardize ZAP duplex port to 9999
ZAP binary protocol duplex RPC standardizes on :9999 across every
service. Lux validator staking/RPC ports (9630/9631 mainnet, 9650/9651
devnet) are unchanged — those are luxd network protocol ports, not ZAP.

One ZAP port everywhere (was a mix of 9651/9652/9653/9633/9900).
2026-04-25 15:35:03 -07:00
zeekay 1685a02464 fix(ui): force-add index.html (repo root .gitignore catches *.html)
Without this the Dockerfile's ui-build COPY fails; local builds
worked because index.html existed on disk.
2026-04-23 10:10:02 -07:00
zeekay 5525752427 chore: gitignore tsbuildinfo 2026-04-23 10:04:58 -07:00
zeekay 1d7f1d2cfc fix: StripPrefix on /_/tasks so embedded assets serve correct MIME 2026-04-23 10:04:48 -07:00
zeekay 645eb13f6f feat: routes locked down — /_/tasks UI, /v1/tasks API, /healthz probe
One and one way only:
  /healthz            k8s / docker HEALTHCHECK (Hanzo convention;
                      matches ~/work/hanzo/kms cmd/kmsd)
  /v1/tasks/health    per-service REST health, survives API-gateway
                      multi-service fan-in without colliding
  /v1/tasks/healthz   alias for tooling that expects `healthz`
                      under every path prefix
  /v1/tasks/...       JSON API (namespaces, workflows, schedules)
  /_/tasks/*          embedded Vite SPA (react-router basename)
  /                   404 — no root route

ZAP transport collapsed to ONE service type: _tasks._tcp on :9652.
The separate _tasks-sdk._tcp listener is gone; same opcodes
(0x0050 submit, 0x0051 schedule), one canonical name.

UI routing: vite.config.ts sets `base: '/_/tasks/'` and
BrowserRouter has `basename="/_/tasks"`, so the SPA deep-links
correctly under the /_/ admin mount (Hanzo Base convention).

Added pkg/tasks/embed.go as the composable SDK surface for
running tasks in-process from a Base app:

  srv, _ := tasks.Embed(ctx, tasks.EmbedConfig{DataDir: dir})
  defer srv.Stop(ctx)

The contract is exposed; implementation is stubbed pending task
#41 (internal-gRPC → ZAP rewrite) so the persistence + RPC layer
can move off go.temporal.io imports without breaking callers.
2026-04-23 10:02:24 -07:00
zeekay cad381cde2 feat: kill gRPC-Gateway — gofiber + /v1/tasks/* only
External HTTP surface is now one path prefix: /v1/tasks/*. The
runtime.ServeMux + JSON-over-gRPC transcoding layer is gone.
Browsers get JSON here because they have to; every other caller
uses the ZAP binary transport on :9652 (_tasks-sdk._tcp).

Backend:
  - service/frontend/http_api_server.go rewritten around gofiber
    v3. Four read routes wired directly to WorkflowHandler
    methods via Go function calls — no gRPC codec, no inline
    client conn, no grpc-gateway registration:
      GET /v1/tasks/healthz
      GET /v1/tasks/namespaces
      GET /v1/tasks/namespaces/:ns/workflows
      GET /v1/tasks/namespaces/:ns/workflows/:id
      GET /v1/tasks/namespaces/:ns/schedules
    Embedded SPA catches everything else for client-routed deep
    links.
  - fx.go: operatorHandler, grpcServerOptions.UnaryInterceptors,
    *mux.Router kept as accepted-but-unused until the fx graph
    can be pruned (next commit). No runtime dependency on them.
  - Response marshalling: protojson.MarshalOptions. The request
    and response types are still go.temporal.io/api protos; task
    #41 replaces them with ZAP schemas end-to-end.

UI:
  - Every fetch URL flips /api/v1/ → /v1/tasks/.
  - @hanzogui/lucide-icons-2 → lucide-react. The hanzo-gui
    package was pulling react-native transitively and Vite 8 /
    rolldown chokes on Flow syntax. lucide-react is the same
    icon set, pure React, no native shim.
  - vite.config.ts proxies /v1/tasks → :7234 so the SPA can use
    relative URLs in dev.

Verified: /v1/tasks/namespaces returns "default" namespace over
the gofiber path with gRPC-Gateway unloaded from the binary.
2026-04-23 09:54:12 -07:00
zeekay dc3a3d3712 feat(ui): Vite 8 + React SPA embedded into tasksd (ui/)
Self-contained admin UI for hanzoai/tasks. Lives in ui/, builds to
ui/dist/ via Vite 8, gets embedded into the tasksd binary with
go:embed, and is served at / on the HTTP API port (default :7234).

Stack:
  - Vite 8
  - React 18 + TypeScript strict
  - react-router (client-side routing, deep-link safe)
  - SWR (zero-config fetch + revalidation)
  - Tailwind 3 (dark-first palette; no webfont)
  - @hanzogui/lucide-icons-2 (shared Hanzo icon set)

v1 pages:
  - /namespaces              — list all namespaces + retention
  - /namespaces/:ns/workflows       — running + recent executions
  - /namespaces/:ns/workflows/:id   — describe single execution
  - /namespaces/:ns/schedules       — schedule list + spec

The UI talks to the existing Temporal HTTP gateway at /api/v1/*
(auto-generated from the workflowservice + operatorservice protos),
so no new backend routes were added. mux.Router registers /api/*
first, then falls through to ui.Handler() for everything else; the
SPA's client-side router takes over for unknown paths.

Build integration:
  - ui/embed.go exposes Handler() over an embed.FS rooted at dist/
  - Dockerfile adds a node:22-alpine UI build stage that produces
    ui/dist, then the Go build stage overlays that onto the
    placeholder dist/index.html already committed (so `go build`
    works on a developer laptop without running pnpm first).
  - service/frontend/http_api_server.go imports tasksui and mounts
    at /. The existing catch-all was split:
      1. PathPrefix("/api/") → Temporal gRPC-Gateway
      2. PathPrefix("/")     → embedded SPA
  - Anything that isn't a registered /api/... handler renders the
    SPA shell, which lets react-router resolve the path.

Dev workflow:
  pnpm --prefix ui install
  pnpm --prefix ui dev          # Vite on :5173, proxies /api to :7234
  go run ./cmd/tasksd embedded-sqlite  # :7234 backs the API

Next: ZAP port for the internal gRPC services (task #41).
2026-04-23 07:28:51 -07:00