You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 samepackages/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:
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)
Problem
apps/loopover-miner-uiis functionally complete but visibly behind the rest of the design system, and has no conversational surface at all:apps/loopover-miner-ui/index.html:3hardcodesclass="dark" style="color-scheme: dark"on<html>with no theme switcher UI, even thoughpackages/loopover-ui-kit/src/theme.cssalready 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:8still 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, andpagination.tsxall ship in the shared kit but are imported nowhere underapps/loopover-miner-ui/src— every loading state is plain text ("Loading local run state…"atrun-history.tsx:27,"Loading local portfolio queue…"atportfolio.tsx:40,"Loading local ledgers…"atledgers.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.run-history.tsx:26-42vsportfolio.tsx:39-56vsledgers.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-39is one<header>plus a flat 4-link<nav>with no active-route highlighting —packages/loopover-ui-kit/src/components/sidebar.tsxships a full sidebar component that goes entirely unused.@loopover/ui-kit's 45-component set (packages/loopover-ui-kit/src/components/*.tsx) — the nearest thing iscommand.tsx, a cmdk-style filtered palette with click-to-navigate, not a message thread withEnter-to-submit.Meanwhile, AMS already has the local data richness to justify a real redesign: 17
node:sqlitestores 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 fordiscover/attempt.Proposal
Keep the layout hybrid and dashboard-first, not a chat-replaces-navigation rewrite:
__root.tsx's routedOutletand 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.tsxso it survives route navigation. On narrow viewports it collapses to a slide-over by reusingpackages/loopover-ui-kit/src/components/sidebar.tsx's existing mobile-sheet mechanism for that one responsive behavior only —sidebar.tsxitself is not adopted as primary navigation.The work splits into two independently-progressing tracks:
StateBoundary/Skeleton/chart/pagination adoption across all four routes, plus wiring the theme toggle thattheme.cssalready supports. This touches no new attack surface and reuses mature, already-tested components.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 independentPreToolUsedeny-hook (packages/loopover-miner/lib/pretooluse-hook.js) that the Claude Agent SDK guarantees runs even underbypassPermissions. 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 fromportfolio.tsx:97-162)/api/governor/{pause,resume}(already exists — wired fromledgers.tsx's governor-control section)discover/attempt, since those two are CLI-only today (packages/loopover-miner/discover-cli.js,attempt-cli.js— no HTTP route exists yet)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: truerather than heldmaintainer-onlypending a further spec pass. This mirrors how#6208,#6209, and#6230will 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/*.tsfetchers (apps/loopover-miner-ui/src/lib/run-history.ts,portfolio-queue.ts,ledgers.ts), theusePolledFetchpoll cadence (apps/loopover-miner-ui/src/lib/use-polled-fetch.ts,DEFAULT_POLL_INTERVAL_MSat 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 viaaddBlockedBy:Visual/ui-kit track
LoadingState/EmptyState/ErrorState/StateBoundary/Spinnerfromapps/loopover-ui/src/components/site/state-views.tsxinto@loopover/ui-kitproper (prerequisite for Miner dashboard chat: message list, composer, and streaming renderer #6834 below)__root.tsxheader/nav visual overhaul, active-route highlighting, theme toggle wiring, and theindex.htmltitle fix (Gittensory Miner→ current brand)index.tsxskeleton/chart adoption on the threeSummaryCardsrun-history.tsxskeleton and pagination adoptionportfolio.tsxskeleton/chart/pagination adoption across its status cards, per-repo table, and queue-actions tableledgers.tsxskeleton/chart/pagination adoption across its claims cards and event tablesChat track
__root.tsx, slide-over on narrow viewports viasidebar.tsx's mobile-sheet mechanismloopover_miner_*MCP tools into chat responses, no new tool surface/api/portfolio-queue/{release,requeue}routes (blocked by Miner dashboard chat: action-dispatch scaffolding (config flag, shared dispatch layer) #6836)/api/governor/{pause,resume}routes (blocked by Miner dashboard chat: action-dispatch scaffolding (config flag, shared dispatch layer) #6836)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— routedOutlet, header, nav, mount point for the chat railapps/loopover-miner-ui/src/routes/index.tsx,run-history.tsx,portfolio.tsx,ledgers.tsx— the four routes staying in placeapps/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 trackapps/loopover-miner-ui/index.html,apps/loopover-miner-ui/src/styles.css— theme hardcoding and stale title to fixpackages/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 adoptapps/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 primitivespackages/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 throughpackages/loopover-engine/src/miner/local-write-tools.ts— confirms writes always execute with the miner's own local credentialspackages/loopover-miner/bin/loopover-miner-mcp.js— the 11 read-onlyloopover_miner_*tools chat grounds on in v1packages/loopover-miner/discover-cli.js,attempt-cli.js— CLI-only commands needing new mirrored API routes#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:
__root.tsx's routedOutletas primary contentloopover_miner_*tools, no new tool surface (packages/loopover-miner/lib/prediction-ledger.js,calibration.js,ranked-candidates.jsstay unwired this pass)sidebar.tsxas primary navigation — it is reused only for its mobile-sheet slide-over mechanism on the chat railpackages/loopover-ui-kit/src/theme.cssstay as-is; only the already-defined.dark-class toggle gets wired up#6230's separate maintainer-facing chat scope (different audience, different backend/placement/cost questions, all still open there)