Skip to content

feat(ui): add network-vs-API error distinction and loading skeletons to StateBoundary - #5620

Merged
JSONbored merged 2 commits into
JSONbored:mainfrom
claytonlin1110:feat/miner-ui-overview-4853
Jul 13, 2026
Merged

feat(ui): add network-vs-API error distinction and loading skeletons to StateBoundary#5620
JSONbored merged 2 commits into
JSONbored:mainfrom
claytonlin1110:feat/miner-ui-overview-4853

Conversation

@claytonlin1110

@claytonlin1110 claytonlin1110 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Several routes had minimal state handling: no retry, no way to tell "we couldn't reach the server" apart from "the server answered with an error", and no content-shaped loading placeholder — app.runs.tsx in particular rendered its loading state identically to a genuinely-empty one (it showed "No runs match these filters" before the first fetch had even completed).
  • useApiResource now surfaces the underlying apiFetch failure kind/status, and StateBoundary/ErrorState use it to render distinct copy + iconography for a connectivity failure (WifiOff, "Can't reach the server") vs a server-side error (AlertTriangle, generic "Couldn't load data"). StateBoundary also accepts an optional loadingSkeleton to replace the generic spinner with a content-shaped placeholder.
  • Both additions are purely additive and backward-compatible — every existing StateBoundary/ErrorState/useApiResource consumer (app.analytics, app.operator, the maintainer/owner/miner/digest/commands panels, dead-letter-queue-panel, notification-readiness-card) keeps its current default copy unless it opts in to the new props.
  • Wired the new primitives into app.runs.tsx, the most minimal of the routes named in the issue: replaced the old ad-hoc "Live runs are unavailable" banner with a proper StateBoundary (retry, network-aware error copy, loading skeleton), and added a RefreshMeta "last refresh" label + manual refresh button to the header.

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked a currently open issue this PR resolves (e.g. Closes #123) — a linked open issue is required for every contributor PR.

Closes #793

Validation

  • git diff --check
  • npm run actionlint — ran as part of npm run test:ci, passed
  • npm run db:migrations:check / db:schema-drift:check / selfhost:env-reference:check — ran as part of npm run test:ci, passed (no DB/env changes in this PR)
  • npm run typecheck
  • npm run test:coverage locally (this PR only touches apps/gittensory-ui/**, which Codecov's src/**-only patch rule does not gate, but the new/changed logic is covered by new and updated tests)
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:test
  • npm audit --audit-level=moderate
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

If any required check was skipped, explain why:

  • npm run cf-typegen:check (part of npm run test:ci) fails locally on this Windows dev machine with a wrangler ENOENTscripts/gen-cf-typegen.mjs calls execFileSync("wrangler", ...) without shell: true, which cannot invoke npm's .cmd shim on Windows (confirmed in isolation, independent of any change in this PR — reproduces with a bare execFileSync("wrangler", ["--version"]) on a clean checkout). This PR makes no Cloudflare/wrangler.jsonc changes; the check runs fine on the actual CI (Linux runners).

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. (N/A — no auth/session/CORS surface touched.)
  • API/OpenAPI/MCP behavior is updated and tested where needed. (N/A — no backend/API route changes; errorKind/errorStatus are client-side fields derived from the existing apiFetch result shape.)
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks.
  • Visible UI changes include a UI Evidence section below with JPG/JPEG or PNG screenshots arranged as organized, captioned, clickable thumbnails. SVG screenshots are not used as review evidence. Review-only screenshots or recordings are not committed to the repository.
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs. (N/A.)

UI Evidence

All captured against app.runs.tsx with a mocked authenticated session and a mocked /v1/agent/runs response, so each state is genuinely reachable rather than staged.

State / title JPG/PNG evidence
Loading (content-shaped skeleton, replaces the old spinner-only state) Loading skeleton
Network error (new: distinct copy + WifiOff icon + retry) Network error state
HTTP error (generic copy + AlertTriangle icon + retry) HTTP error state
HTTP error, mobile layout (375×812) HTTP error state, mobile

Notes

  • Scoped to the shared primitives (state-views.tsx, use-api-resource.ts) plus app.runs.tsx, the route with the most clearly minimal state handling of the ones named in the issue. app.workbench.tsx's panels (miner/playground/commands/digest) and app.operator.tsx/app.analytics.tsx already use StateBoundary; they pick up the network-vs-API distinction and skeleton support automatically the next time they're touched, with no migration required since both additions are opt-in.

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@claytonlin1110
claytonlin1110 marked this pull request as draft July 13, 2026 11:04
@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 13, 2026
@loopover-orb

loopover-orb Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-13 14:24:54 UTC

5 files · 1 AI reviewer · no blockers · readiness 100/100 · CI green · clean

⏸️ Suggested Action - Manual Review

  • AI review could not be completed: The dual-model AI review did not return a usable verdict for this change.

Review summary
This wires a real errorKind/loadingSkeleton distinction through useApiResource → StateBoundary → ErrorState, and applies it to app.runs.tsx, replacing an ad-hoc inline error banner and a loading state that was previously indistinguishable from an empty one (issue #793's core complaint). The default-preserving `??` fallback chains in StateBoundary/ErrorState are correctly ordered (explicit title/description always wins, then errorKind-derived copy, then the pre-#793 default) and are exercised by both-arm tests. The one wrinkle: app.runs.tsx always passes the raw `liveRuns.error` string as `errorDescription`, so the new friendly 'We couldn't reach the API…' network copy the PR advertises never actually surfaces on the one route wired up — only the title/icon change.

Nits — 6 non-blocking
  • apps/gittensory-ui/src/routes/app.runs.tsx (StateBoundary props): `errorDescription={... liveRuns.error}` is always a truthy string when isError, so it permanently overrides ErrorState's network-aware description default — only the title/icon actually change for a network failure on this route; consider leaving `errorDescription` unset here so the friendly connectivity copy shows, or intentionally document why the raw message is preferred.
  • apps/gittensory-ui/src/lib/api/use-api-resource.ts: the new `errorStatus` field is populated but never consumed by state-views.tsx or app.runs.tsx in this diff — fine to land ahead of use, but worth a one-line note on the intended consumer.
  • No test directly exercises the app.runs.tsx wiring itself (isLoading/isError/errorKind derivation, RunsListSkeleton rendering) — coverage is thorough for the shared primitives but the route-level wiring is only manually reasoned about.
  • apps/gittensory-ui/src/components/site/state-views.tsx: `NETWORK_ERROR_KINDS` treats `timeout` the same as `network`; confirm that's intentional product copy (a slow server vs. an unreachable one) rather than an oversight.
  • Drop the hardcoded `errorDescription={liveRuns.error}` in app.runs.tsx or move the raw message into a secondary/detail line so the polished network-vs-server copy this PR adds is actually visible on the wired route.
  • AI review could not be completed — The gate is held for a human reviewer rather than passed automatically; it re-evaluates on the next update.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #793
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 462 registered-repo PR(s), 318 merged, 103 issue(s).
Contributor context ✅ Confirmed Gittensor contributor claytonlin1110; Gittensor profile; 462 PR(s), 103 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Improvement ✅ Minor risk: clean · value: minor — Code changes are accompanied by test evidence. LLM value judgment: moderate — It's a focused, backward-compatible enhancement to shared state primitives with solid test coverage that closes a real, previously-reported UX gap in one route, but the value is narrowed by the route wiring not actually surfacing the new network-specific copy it advertises.
Review context
  • Author: claytonlin1110
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, JavaScript, TypeScript, Rust
  • Official Gittensor activity: 462 PR(s), 103 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Keep the PR focused and include validation evidence before maintainer review.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
[BETA] Chat with Gittensory

Ask Gittensory a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @gittensory ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @gittensory chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @gittensory mention with a real question is routed to the closest matching read-only command automatically -- no exact syntax required.

Full command reference: https://gittensory.aethereal.dev/docs/gittensory-commands

Visual preview
Route Viewport Before (production) After (this PR's preview) Diff
/app/runs desktop before /app/runs after /app/runs
/app/runs mobile before /app/runs (mobile) after /app/runs (mobile)

Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

…to StateBoundary

Several routes had minimal loading/error handling: no retry, no way to tell a
connectivity failure apart from a server-side error, and a loading state that
rendered identically to a genuinely-empty one. useApiResource now surfaces the
apiFetch failure kind and status; StateBoundary/ErrorState use it to show
distinct copy and iconography for "can't reach the server" vs "the server
returned an error", and accept an optional content-shaped loadingSkeleton in
place of the generic spinner. Both are additive and backward-compatible --
every existing StateBoundary/ErrorState/useApiResource consumer keeps its
current default copy unless it opts in.

Wires the new primitives into app.runs.tsx, the most minimal of the routes
named in the issue: a proper StateBoundary (retry, network-aware error,
loading skeleton) replaces the old ad-hoc warning banner and the misleading
loading state that showed "no runs match filters" before the first fetch
even completed, plus a RefreshMeta "last refresh" label in the header.
@claytonlin1110
claytonlin1110 force-pushed the feat/miner-ui-overview-4853 branch from 8f3f533 to 66c36b6 Compare July 13, 2026 11:44
@claytonlin1110 claytonlin1110 changed the title feat(miner-ui): rewrite the Overview page with real live data feat(ui): add network-vs-API error distinction and loading skeletons to StateBoundary Jul 13, 2026
@loopover-orb loopover-orb Bot added gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. gittensor:priority Maintainer-selected Gittensor priority — scores a 1.5x multiplier. and removed gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. labels Jul 13, 2026
@JSONbored
JSONbored merged commit fb0877a into JSONbored:main Jul 13, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. gittensor:priority Maintainer-selected Gittensor priority — scores a 1.5x multiplier. manual-review Gittensor contributor context

Development

Successfully merging this pull request may close these issues.

feat(ui): cross-cutting error/loading/empty states + retry

2 participants