feat(engine): assemble the customer-facing loop dashboard view - #6136
Conversation
A customer has no place to see their loop's status, spend, and results, distinct from any internal operations view (JSONbored#4807). Its proposal is to surface JSONbored#4800's progress stream and JSONbored#4792's spend/ledger data; both have since landed, so the data the surface needs now exists. Add that surface's pure view model. buildCustomerLoopView assembles one customer's loop into what their dashboard shows: where it is (JSONbored#4800's ProgressSnapshot, passed through untouched), what it has cost them (JSONbored#4792's spend, plus headroom against their JSONbored#4796 allocation), and what came out of it (JSONbored#4801's results payload) -- the submit -> watch progress -> see spend and results path, computed once, so the eventual UI only lays it out. The central guarantee is tenant isolation. JSONbored#4807 exists because a customer's view is distinct from JSONbored#4808's internal ops fleet view: a customer sees THEIR loop and nothing else. So the tenant filter runs here, via JSONbored#4792's own audited totalConsumptionForTenant, rather than trusting a caller to have pre-filtered -- a dashboard that renders one customer another's spend is the worst bug this surface could have, and "the caller filtered it" is not a defense. A test passes another tenant's rows in and asserts they reach neither the totals nor the serialized view. Only JSONbored#4796's spend dimensions are read from the quota decision. Concurrency is not reported: a spend view has no honest activeLoops reading, and inventing one would make the customer's "within allocation" answer depend on a number nobody measured -- the same rule JSONbored#4792 follows. evaluateTenantQuota still computes the headroom, so the customer's figures and the enforcement path cannot disagree. Assembles a view only: no fetching, no rendering, no clock read. Building the surface is the separate UI work, which the issue gates on the shared design system (JSONbored#4966/JSONbored#4967), so this core carries no styling or framework opinion and touches no UI path. Invariants tested: no quota reports null rather than a fabricated ceiling, so a customer can tell "no limit is set" from "you have room"; a concurrency-only limit never makes a within-allocation customer read as over spend; and resultsReady requires both a finished loop and a real payload, so the dashboard never invites a customer to see results that do not exist yet. Closes JSONbored#4807
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6136 +/- ##
=======================================
Coverage 95.33% 95.33%
=======================================
Files 598 599 +1
Lines 47177 47185 +8
Branches 15025 15032 +7
=======================================
+ Hits 44974 44982 +8
Misses 1477 1477
Partials 726 726
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Tip 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-15 12:40:37 UTC
✅ Suggested Action - Approve/Merge
Review summary Nits — 5 non-blocking
Linked issue satisfactionPartially addressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://gittensory.aethereal.dev/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 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.
|
Summary
Closes #4807
A customer has no place to see their loop's status, spend, and results — "distinct from any internal operations view." The issue's proposal is to surface #4800's progress stream and #4792's spend/ledger data; both have since landed, so the data this surface needs now exists.
This adds that surface's pure view model.
buildCustomerLoopView(input)assembles one customer's loop into what their dashboard shows — the submit → watch progress → see spend and results path, computed once, so the eventual UI only lays it out:progressProgressSnapshot, passed through untouched — the customer already sees exactly this in the streamspendtotalConsumptionForTenant, plus headroom against their #4796 allocationresults/resultsReadyResultsPayload, surfaced only once it really existsThe central guarantee: tenant isolation
#4807 exists because a customer's view is distinct from #4808's internal ops fleet view: a customer sees their loop and nothing else. So the tenant filter runs here, via #4792's own audited
totalConsumptionForTenant, rather than trusting a caller to have handed in a pre-filtered list. A dashboard that renders one customer another's spend is the worst bug this surface could have, and "the caller filtered it" is not a defense. A test passes another tenant's rows in and asserts they reach neither the totals nor the serialized view.One deliberate omission
Only #4796's spend dimensions are read from the quota decision. Its third dimension, concurrency, is not reported: a spend view has no honest
activeLoopsreading, and inventing one would make the customer's "within allocation" answer depend on a number nobody measured — the same rule #4792'stotalConsumptionForTenantfollows for exactly this reason.evaluateTenantQuotastill computes the headroom, so the customer's figures and the enforcement path can never disagree about their allocation. A test pins this: a concurrency-only limit never makes a within-allocation customer read as over spend.Scope — what this deliberately does not do
It assembles a view only: no fetching, no rendering, no clock read. Building the surface is the separate UI work, which the issue additionally gates on the shared design system (#4966/#4967) — "do not scaffold a third divergent styling approach." So this core carries no styling or framework opinion, touches no UI path, and stays correct whatever renders it. This is the same pure-decision-core shape the sibling Rent-a-Loop issues merged as (#4801, #4800, #4806, #4796, #4792, #4808).
feat(engine): …).CONTRIBUTING.md; nosite//CNAME/lovable; no changelog edit.Validation
git diff --checkclean.npm run build:miner— exit 0 (the engine package compiles + emits).npm run typecheck— exit 0.npx vitest runover this module and every sibling it composes (loop-consumption,tenant-quota,loop-progress,results-payload) — 49 tests passed, so the reused primitives still hold.packages/loopover-engine/src/customer-loop-view.ts(in Codecov'scoverage.include): 100% statements (5/5), 100% branch (16/16), 100% functions (1/1), 100% lines (5/5).main.Invariants a customer-facing surface must not get wrong — each tested:
remaining/withinQuotaarenullactiveLoopsinput, so it must not answer that questionresultsReadyneeds a finished loop and a real payload0, notundefinedSafety
Date.now(), so it is deterministic and trivially testable.apps/**) — despite closing avisual-labelled issue, this PR touches onlypackages/loopover-engine/src/**andtest/**; no changelog edit.