Skip to content

Miner dashboard redesign: visual overhaul + action-capable chat (epic) #6504

Description

@JSONbored

Problem

apps/loopover-miner-ui is functionally complete but visibly behind the rest of the design system, and has no conversational surface at all:

  • apps/loopover-miner-ui/index.html:3 hardcodes class="dark" style="color-scheme: dark" on <html> with no theme switcher UI, even though packages/loopover-ui-kit/src/theme.css already defines full light tokens under :root (lines 107-169) and a working .dark-class toggle mechanism (lines 172-228, @custom-variant dark (&:is(.dark *)); at line 17) — the toggle exists in the shared package, it's just never wired up here.
  • apps/loopover-miner-ui/index.html:8 still reads <title>Gittensory Miner</title> — a stale-brand leak visible in every browser tab, left over from the completed repo rename.
  • packages/loopover-ui-kit/src/components/skeleton.tsx, chart.tsx, and pagination.tsx all ship in the shared kit but are imported nowhere under apps/loopover-miner-ui/src — every loading state is plain text ("Loading local run state…" at run-history.tsx:27, "Loading local portfolio queue…" at portfolio.tsx:40, "Loading local ledgers…" at ledgers.tsx:118), every metric is a bare number in a <dd>/<span> (index.tsx:24, portfolio.tsx:64-66), and every table (run-history.tsx:44-63, portfolio.tsx:71-92,129-158, ledgers.tsx:41-57,178-195) is a static full dump with no sort/filter/pagination.
  • All four routes hand-duplicate the identical 4-branch loading/error/empty/data JSX literal (run-history.tsx:26-42 vs portfolio.tsx:39-56 vs ledgers.tsx:117-134) instead of sharing one component — this both looks dated and makes every future empty/skeleton upgrade a 4x edit.
  • __root.tsx:8-39 is one <header> plus a flat 4-link <nav> with no active-route highlighting — packages/loopover-ui-kit/src/components/sidebar.tsx ships a full sidebar component that goes entirely unused.
  • No chat-shaped component exists anywhere in this app or in @loopover/ui-kit's 45-component set (packages/loopover-ui-kit/src/components/*.tsx) — the nearest thing is command.tsx, a cmdk-style filtered palette with click-to-navigate, not a message thread with Enter-to-submit.

Meanwhile, AMS already has the local data richness to justify a real redesign: 17 node:sqlite stores under ~/.config/loopover-miner/*.sqlite3 (packages/loopover-miner/lib/local-store.js), a mature two-layer write-safety system (packages/loopover-engine/src/governor/chokepoint.ts, packages/loopover-miner/lib/governor-chokepoint.js, packages/loopover-miner/lib/pretooluse-hook.js), and an entirely read-only 11-tool MCP surface (packages/loopover-miner/bin/loopover-miner-mcp.js: ping, get_portfolio_dashboard, get_manage_status, list_claims, get_audit_feed, get_run_state, list_plans, get_plan, get_governor_decisions, status, get_calibration_report) that a chat surface can ground on immediately with no new tool-building.

Area

apps/loopover-miner-ui (all four routes plus __root.tsx), packages/loopover-ui-kit (one primitive port), a net-new chat surface (composer, message list, streaming renderer, typing indicator, backend grounding, action-dispatch), and two new API routes mirroring the existing action-button pattern for discover/attempt.

Proposal

Keep the layout hybrid and dashboard-first, not a chat-replaces-navigation rewrite: __root.tsx's routed Outlet and all four existing routes (index/run-history/portfolio/ledgers) stay the primary content area, restyled in place — not collapsed into rail-card summaries. If chat is disabled or breaks, the routed dashboard underneath keeps working unchanged.

A persistent, collapsible chat rail (~380px) mounts once in __root.tsx so it survives route navigation. On narrow viewports it collapses to a slide-over by reusing packages/loopover-ui-kit/src/components/sidebar.tsx's existing mobile-sheet mechanism for that one responsive behavior only — sidebar.tsx itself is not adopted as primary navigation.

The work splits into two independently-progressing tracks:

  • Visual/ui-kit track — header/nav restyle plus StateBoundary/Skeleton/chart/pagination adoption across all four routes, plus wiring the theme toggle that theme.css already supports. This touches no new attack surface and reuses mature, already-tested components.
  • Chat track — composer, message list, streaming renderer, typing indicator, read-only backend grounding, and action-dispatch. This is net-new surface with real safety scope.

⚠️ Read this before starting. There is exactly one hard cross-track dependency: apps/loopover-ui/src/components/site/state-views.tsx's LoadingState/EmptyState/ErrorState/StateBoundary (plus its bare Spinner) must be ported from app-local (apps/loopover-ui) into @loopover/ui-kit proper before any chat component that renders its own async state — specifically the message list — is built. Chat must consume the same primitive the ui-kit port produces, not invent a second one. This was confirmed reusable as-is by the #6244 audit (apps/loopover-ui/src/chat-ui-primitives-audit.md, merged via #6474). A child chat issue that hand-rolls its own loading/error/empty JSX instead of waiting on and importing the ported primitive does not satisfy this epic's design.

Chat is action-capable in v1, not deferred to a read-only-then-expand v2. This is a deliberate reversal of the usual default-to-read-only caution, justified because the safety mechanism is already built and mature: every write already executes with the miner's own local credentials (packages/loopover-engine/src/miner/local-write-tools.ts:1-4,17-18 — loopover's MCP tools only ever build action specs; the miner's own local harness runs them), and every write already passes the fail-closed Governor chokepoint (packages/loopover-engine/src/governor/chokepoint.ts:144-365, precedence ladder: kill-switch → dry-run → rate-limit → budget/turn/termination cap → non-convergence → self-reputation-throttle → self-plagiarism → allow) plus the independent PreToolUse deny-hook (packages/loopover-miner/lib/pretooluse-hook.js) that the Claude Agent SDK guarantees runs even under bypassPermissions. A chat input in front of controls that already exist as buttons creates no new privilege — it only changes the input modality to something already fully governed.

The scope is narrow and closed, not an open question for a contributor to resolve: chat can trigger discover/attempt, portfolio release/requeue, and governor pause/resume — strictly by calling the same endpoints the existing buttons already call:

  • /api/portfolio-queue/{release,requeue} (already exists — apps/loopover-miner-ui/src/lib/portfolio-queue-actions.ts, wired from portfolio.tsx:97-162)
  • /api/governor/{pause,resume} (already exists — wired from ledgers.tsx's governor-control section)
  • new routes mirroring that same pattern for discover/attempt, since those two are CLI-only today (packages/loopover-miner/discover-cli.js, attempt-cli.js — no HTTP route exists yet)

⚠️ Read this before starting. Every chat-triggered action must route through the same packages/loopover-engine/src/governor/chokepoint.ts / PreToolUse deny-hook path every other local write already goes through. A child action-dispatch issue that adds a parallel execution path, a bypass flag, or calls the underlying CLI/store directly instead of going through the existing chokepoint-gated endpoint does not satisfy this epic's design, regardless of how the UI looks.

Per house default-off-for-new-and-risky convention, action-dispatch ships behind a config flag (off by default, flippable per-install) independent of the safety verdict above — the safety case justifies building it, not skipping the flag.

Because this action-capability decision names the exact action families, the exact endpoints, the exact safety mechanism, and the exact rollout discipline — with no remaining "what should this look like" question — the action-dispatch child issues (a shared dispatch-layer scaffolding issue and its three action-family children) are contributorEligible: true rather than held maintainer-only pending a further spec pass. This mirrors how #6208, #6209, and #6230 will each become contributor-eligible only once their own currently-open questions get an equivalent closed resolution — unlike this epic's decision, all three of those still have open questions (identity linkage/data path/weighting for #6208; detection mechanism/allowlist/auth model for #6209; audience/backend/placement/cost model for #6230). The read-only chat backend and UI primitives (composer, message list, streaming renderer, typing indicator, backend grounding against the 11 existing read-only tools) are independently contributor-eligible on ordinary "clear existing precedent" grounds — they were never an open design question in the first place.

Every route redesign is restyle-only: the underlying lib/*.ts fetchers (apps/loopover-miner-ui/src/lib/run-history.ts, portfolio-queue.ts, ledgers.ts), the usePolledFetch poll cadence (apps/loopover-miner-ui/src/lib/use-polled-fetch.ts, DEFAULT_POLL_INTERVAL_MS at line 5), and the existing release/requeue/pause/resume action buttons are untouched by the visual-track child issues. Chat's action-dispatch issues call through those exact same button-backed endpoints later, so there is only ever one write path to audit, not two.

Deliverables

This epic itself ships no code. Its deliverable is the decision above plus a fully-linked child-issue tree.

Filed 2026-07-17 as native GitHub sub-issues (linked via addSubIssue), with the two named dependencies linked via addBlockedBy:

Visual/ui-kit track

Chat track

Test Coverage Requirements

Not applicable — this tracker issue has no diff of its own. Each child issue carries its own Codecov patch-coverage requirement per the heavy template.

Expected Outcome

A single documented layout/scope decision that every child issue can be scoped against without re-litigating "should chat replace navigation" or "should v1 be read-only," so child issues can be filed, worked, and reviewed independently and in parallel except for the one named ui-kit-port → message-list dependency.

Links & Resources

  • apps/loopover-miner-ui/src/routes/__root.tsx — routed Outlet, header, nav, mount point for the chat rail
  • apps/loopover-miner-ui/src/routes/index.tsx, run-history.tsx, portfolio.tsx, ledgers.tsx — the four routes staying in place
  • apps/loopover-miner-ui/src/lib/use-polled-fetch.ts, run-history.ts, portfolio-queue.ts, portfolio-queue-actions.ts, ledgers.ts — untouched by the visual track, reused as-is by the action-dispatch track
  • apps/loopover-miner-ui/index.html, apps/loopover-miner-ui/src/styles.css — theme hardcoding and stale title to fix
  • packages/loopover-ui-kit/src/theme.css — Lovable Lime tokens (oklch), existing .dark-class toggle mechanism, stays as-is (no rebrand this pass)
  • packages/loopover-ui-kit/src/components/skeleton.tsx, chart.tsx, pagination.tsx, sidebar.tsx, command.tsx — existing, mature, unused-in-miner-ui components this epic's children adopt
  • apps/loopover-ui/src/components/site/state-views.tsx — the primitive to port (#6244's audit, landed via #6474)
  • apps/loopover-ui/src/chat-ui-primitives-audit.md — full audit of reusable-as-is / needs-adaptation / entirely-absent chat primitives
  • packages/loopover-engine/src/governor/chokepoint.ts, packages/loopover-miner/lib/governor-chokepoint.js, packages/loopover-miner/lib/pretooluse-hook.js — the safety path every chat action must route through
  • packages/loopover-engine/src/miner/local-write-tools.ts — confirms writes always execute with the miner's own local credentials
  • packages/loopover-miner/bin/loopover-miner-mcp.js — the 11 read-only loopover_miner_* tools chat grounds on in v1
  • packages/loopover-miner/discover-cli.js, attempt-cli.js — CLI-only commands needing new mirrored API routes
  • Related, not blocking: #6230 (maintainer chat spec, separate scope — explicitly excluded from this epic), #6208, #6209 (cross-system bridges, both still open-question), #6244/#6474 (chat-adjacent primitives audit this epic's ui-kit-port child issue completes)

Boundaries

Explicitly out of scope for this epic and every child issue under it:

  • Collapsing routed pages into rail-card summaries, or any nav-is-chat rewrite that removes __root.tsx's routed Outlet as primary content
  • New API wiring for calibration, ranked-candidates, or track-record data — v1's chat grounding stays the 11 existing read-only loopover_miner_* tools, no new tool surface (packages/loopover-miner/lib/prediction-ledger.js, calibration.js, ranked-candidates.js stay unwired this pass)
  • sidebar.tsx as primary navigation — it is reused only for its mobile-sheet slide-over mechanism on the chat rail
  • Any bypass-chokepoint execution path, parallel write route, or new credential handling for chat actions
  • Lovable's Draw-to-Build, click-to-edit visual editing, or floating draggable toolbar — those are Lovable-specific builder-UI patterns, not part of this redesign
  • A palette/brand rebrand — the existing "Lovable Lime" oklch tokens in packages/loopover-ui-kit/src/theme.css stay as-is; only the already-defined .dark-class toggle gets wired up
  • Anything touching #6230's separate maintainer-facing chat scope (different audience, different backend/placement/cost questions, all still open there)

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

Status
Done

Relationships

None yet

Development

No branches or pull requests

Issue actions