Skip to content

feat(api): surface per-tenant storage row-counts on the operator dashboard#7201

Merged
loopover-orb[bot] merged 1 commit into
mainfrom
worktree-ai-usage-storage-by-tenant
Jul 18, 2026
Merged

feat(api): surface per-tenant storage row-counts on the operator dashboard#7201
loopover-orb[bot] merged 1 commit into
mainfrom
worktree-ai-usage-storage-by-tenant

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

Closes #4890

Test plan

  • npm run typecheck — clean
  • npm run test:ci (full local gate, unsharded) — green
  • New unit tests in test/unit/ai-usage-tenant.test.ts: groups/orders by tenant correctly, excludes self-host (null-tenant) rows and rows outside the time window, returns [] (not an error) with no hosted rows
  • New UI tests in apps/loopover-ui/src/routes/app.operator.test.tsx: no section when the field is absent/empty, renders each tenant's formatted row count highest-first
  • 100% branch coverage on every line touched in src/db/repositories.ts (mirrors the already-covered listAiCostByTenantSince shape 1:1)
  • UI Evidence — dashboard-only addition following an already-shipped sibling panel's exact visual pattern (AI cost by tenant); no new visual pattern introduced

…board

The account-wide D1 storage cap already has alerting (src/selfhost/d1-size-probe.ts,
70%/90% Prometheus alerts shipped as a direct follow-up to the #3810 incident), but
it's table-level only -- no way to attribute usage to a specific tenant. This issue
was already re-scoped to that specific gap (see the issue's own narrowing comment).

Adds listRowCountByTenantSince (src/db/repositories.ts), a per-installation row-count
breakdown of ai_usage_events -- the one high-growth table with a clean installationId
column today, same GROUP BY shape as the existing listAiCostByTenantSince. Wires it
into the operator dashboard payload and a new "Storage by tenant" UI section,
following the exact same pattern PR #7191 established for AI cost by tenant. Empty
for self-host, as with its sibling.

Closes #4890
@superagent-security

Copy link
Copy Markdown
Contributor

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

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
loopover-ui c5cc0e5 Commit Preview URL

Branch Preview URL
Jul 18 2026, 12:10 PM

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 1.01kB (0.01%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
loopover-ui 7.59MB 1.01kB (0.01%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: loopover-ui

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/add-scalar-classes-D8qSyICf.js (New) 2.17MB 2.17MB 100.0% 🚀
assets/tanstack-vendor-CLVvrpjw.js (New) 805.87kB 805.87kB 100.0% 🚀
assets/docs.fumadocs-spike-api-reference-YAdAHbRv.js (New) 442.94kB 442.94kB 100.0% 🚀
assets/AgentScalarChatInterface.vue-CP0hVM3r.js (New) 201.71kB 201.71kB 100.0% 🚀
assets/modal-BKlgcpKi.js (New) 184.28kB 184.28kB 100.0% 🚀
assets/client-BrYRUQSX.js (New) 146.06kB 146.06kB 100.0% 🚀
assets/maintainer-panel-DZsF8R8S.js (New) 78.53kB 78.53kB 100.0% 🚀
assets/ui-vendor-PwxAf4FD.js (New) 57.05kB 57.05kB 100.0% 🚀
assets/routes-DZVXqIz3.js (New) 34.29kB 34.29kB 100.0% 🚀
assets/owner-panel-7V0M6zSL.js (New) 26.89kB 26.89kB 100.0% 🚀
assets/app-CGsGog6Q.js (New) 25.65kB 25.65kB 100.0% 🚀
assets/app.runs-BoLiO5VM.js (New) 23.55kB 23.55kB 100.0% 🚀
assets/miner-panel-DAocbbV6.js (New) 20.25kB 20.25kB 100.0% 🚀
assets/docs-source-Ccn_n7xT.js (New) 19.3kB 19.3kB 100.0% 🚀
assets/api._op-jjctmF6K.js (New) 17.82kB 17.82kB 100.0% 🚀
assets/self-hosting-docs-audit-B8dQrSCm.js (New) 16.73kB 16.73kB 100.0% 🚀
assets/app.index--9dmbX7B.js (New) 15.62kB 15.62kB 100.0% 🚀
assets/playground-panel-BI4vtKGg.js (New) 14.35kB 14.35kB 100.0% 🚀
assets/extension-DqHfhR8J.js (New) 10.04kB 10.04kB 100.0% 🚀
assets/app.config-generator-D9XCRRNX.js (New) 9.86kB 9.86kB 100.0% 🚀
assets/app.audit-BU1ivPko.js (New) 9.54kB 9.54kB 100.0% 🚀
assets/maintainers-DRgwwYbj.js (New) 8.06kB 8.06kB 100.0% 🚀
assets/miners-lUoxS1QQ.js (New) 7.89kB 7.89kB 100.0% 🚀
assets/agents-_3x1tnJ5.js (New) 7.76kB 7.76kB 100.0% 🚀
assets/commands-panel-d2Ux1xn-.js (New) 6.63kB 6.63kB 100.0% 🚀
assets/roadmap-CpadOY1N.js (New) 6.33kB 6.33kB 100.0% 🚀
assets/repos._owner._repo.quality-C2hJ4Z2d.js (New) 6.14kB 6.14kB 100.0% 🚀
assets/digest-panel-CrMWQSYT.js (New) 6.14kB 6.14kB 100.0% 🚀
assets/docs.index-BVMkdKeM.js (New) 5.42kB 5.42kB 100.0% 🚀
assets/api.index-CaT6cqey.js (New) 4.74kB 4.74kB 100.0% 🚀
assets/api-D2jJQ2En.js (New) 2.69kB 2.69kB 100.0% 🚀
assets/docs-C11O5mLR.js (New) 2.67kB 2.67kB 100.0% 🚀
assets/app.workbench-RVfG_1wN.js (New) 1.58kB 1.58kB 100.0% 🚀
assets/tooltip-xv7GVrqM.js (New) 1.45kB 1.45kB 100.0% 🚀
assets/session-BlkKoZ_4.js (New) 1.44kB 1.44kB 100.0% 🚀
assets/tabs-BqENggl7.js (New) 1.39kB 1.39kB 100.0% 🚀
assets/app.repos-DCBv5R3I.js (New) 1.07kB 1.07kB 100.0% 🚀
assets/input-BfqX99GO.js (New) 796 bytes 796 bytes 100.0% 🚀
assets/file-cog-C2be4X_V.js (New) 758 bytes 758 bytes 100.0% 🚀
assets/docs.ai-summaries-Dstf3yX7.js (New) 506 bytes 506 bytes 100.0% 🚀
assets/app.maintainer-DJjcSoQr.js (New) 502 bytes 502 bytes 100.0% 🚀
assets/docs.branch-analysis-Yyz2NgEq.js (New) 499 bytes 499 bytes 100.0% 🚀
assets/docs.scoreability-L6K7BfzC.js (New) 499 bytes 499 bytes 100.0% 🚀
assets/docs.upstream-drift-cM1qc1N3.js (New) 499 bytes 499 bytes 100.0% 🚀
assets/docs.federated-fleet-intelligence-DH8uxOqJ.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.maintainer-install-trust-B_6qkUrx.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-ai-providers-rpDBINZ_.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-backup-scaling-CTg_Ryaw.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-configuration-CwpN33zH.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-docs-audit-CZmXvVgW.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-github-app-fd_lnCLA.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-quickstart-DUztEyAB.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-rag-DhAvS_aJ.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-rees-D6BR0lit.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-rees-analyzers-CEH6iR5y.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-release-checklist-CHcDc5YJ.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-releases-Cil3IoNK.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-security-LVALfTs3.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-troubleshooting-BY_2ccVk.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.self-hosting-unified-ams-orb-BHCBkJDI.js (New) 498 bytes 498 bytes 100.0% 🚀
assets/docs.ams-config-precedence-C9G82JsO.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.ams-deployment-Dj2go5_u.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.ams-discovery-plane-DFCwSm5I.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.ams-env-reference-46hTHgns.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.ams-fleet-manifest-BDQXjwWG.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.ams-goal-spec-CmjRCZpN.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.ams-observability-b9MWR011.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.ams-operations-runbook-XQzOKSMd.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.ams-sizing-B_sxjwCJ.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.ams-unattended-scheduling-Dimahbjm.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.beta-onboarding-BVpRNf6H.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.maintainer-self-hosting-CYucmlwN.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.mcp-clients-DFVbxfRg.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.owner-checklist-BtNGRhnF.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.quickstart-DUdUa2rP.js (New) 497 bytes 497 bytes 100.0% 🚀
assets/docs.github-app-BTHVoQO4.js (New) 495 bytes 495 bytes 100.0% 🚀
assets/docs.maintainer-workflow-C9iRCi_L.js (New) 495 bytes 495 bytes 100.0% 🚀
assets/docs.privacy-security-CaS5Ldzi.js (New) 495 bytes 495 bytes 100.0% 🚀
assets/docs.troubleshooting-9TnVRN4o.js (New) 495 bytes 495 bytes 100.0% 🚀
assets/docs.tuning-DmTUTJjl.js (New) 495 bytes 495 bytes 100.0% 🚀
assets/docs.loopover-commands-BQCSIbtR.js (New) 494 bytes 494 bytes 100.0% 🚀
assets/sparkles-iMtWWRS5.js (New) 494 bytes 494 bytes 100.0% 🚀
assets/docs.how-reviews-work-CI5oHWol.js (New) 493 bytes 493 bytes 100.0% 🚀
assets/app.owner-Dxn7vFGP.js (New) 474 bytes 474 bytes 100.0% 🚀
assets/app.commands-DAhaedDo.js (New) 455 bytes 455 bytes 100.0% 🚀
assets/app.playground-1xG_-IdG.js (New) 442 bytes 442 bytes 100.0% 🚀
assets/reveal-DhVcIiMo.js (New) 442 bytes 442 bytes 100.0% 🚀
assets/index-BZeUxIxv.js (New) 438 bytes 438 bytes 100.0% 🚀
assets/app.digest-BsjzpVDG.js (New) 430 bytes 430 bytes 100.0% 🚀
assets/eye-off-u_i4bvnm.js (New) 430 bytes 430 bytes 100.0% 🚀
assets/app.miner-DniBMn2z.js (New) 422 bytes 422 bytes 100.0% 🚀
assets/key-round-BN8LQLp2.js (New) 355 bytes 355 bytes 100.0% 🚀
assets/bot-Czc_u4Uo.js (New) 328 bytes 328 bytes 100.0% 🚀
assets/trash-2-BTdiMujv.js (New) 328 bytes 328 bytes 100.0% 🚀
assets/save-BrMkQ8Eq.js (New) 327 bytes 327 bytes 100.0% 🚀
assets/git-pull-request-arrow-p794riZu.js (New) 321 bytes 321 bytes 100.0% 🚀
assets/shield-check-61wBeV4c.js (New) 320 bytes 320 bytes 100.0% 🚀
assets/wrench-BSYeGFXd.js (New) 303 bytes 303 bytes 100.0% 🚀
assets/list-checks-DOSQaSMu.js (New) 279 bytes 279 bytes 100.0% 🚀
assets/shield-BDB-LEiN.js (New) 272 bytes 272 bytes 100.0% 🚀
assets/workflow-Cuek1BPJ.js (New) 265 bytes 265 bytes 100.0% 🚀
assets/compass-CYEqtUt1.js (New) 251 bytes 251 bytes 100.0% 🚀
assets/history-CEDyF40P.js (New) 237 bytes 237 bytes 100.0% 🚀
assets/activity-CnZeH_f7.js (New) 234 bytes 234 bytes 100.0% 🚀
assets/message-square-BMT6GGRv.js (New) 233 bytes 233 bytes 100.0% 🚀
assets/lock-Dvy0iF0i.js (New) 206 bytes 206 bytes 100.0% 🚀
assets/rotate-cw-DWhMUzrj.js (New) 201 bytes 201 bytes 100.0% 🚀
assets/play-ByiUQ5Yi.js (New) 190 bytes 190 bytes 100.0% 🚀
assets/circle-check-BAhDXW4O.js (New) 178 bytes 178 bytes 100.0% 🚀
assets/search-BJzri3TE.js (New) 174 bytes 174 bytes 100.0% 🚀
assets/self-hosting-docs-audit-CIYUOLhy.js (New) 141 bytes 141 bytes 100.0% 🚀
assets/circle-xhVXVa_V.js (New) 130 bytes 130 bytes 100.0% 🚀
assets/modal-DTFgsD3C.js (New) 115 bytes 115 bytes 100.0% 🚀
assets/add-scalar-classes-rRq8_DuZ.js (Deleted) -2.17MB 0 bytes -100.0% 🗑️
assets/tanstack-vendor-Dm1QoRBf.js (Deleted) -804.85kB 0 bytes -100.0% 🗑️
assets/docs.fumadocs-spike-api-reference-Bc8Vfvkg.js (Deleted) -442.94kB 0 bytes -100.0% 🗑️
assets/AgentScalarChatInterface.vue-DYBxUJ0p.js (Deleted) -201.71kB 0 bytes -100.0% 🗑️
assets/modal-CQb8Kg0Z.js (Deleted) -184.28kB 0 bytes -100.0% 🗑️
assets/client-YgOLfTfO.js (Deleted) -146.06kB 0 bytes -100.0% 🗑️
assets/maintainer-panel-D7fPy7OK.js (Deleted) -78.53kB 0 bytes -100.0% 🗑️
assets/ui-vendor-BX8pLrxi.js (Deleted) -57.05kB 0 bytes -100.0% 🗑️
assets/routes-DRiQSsF9.js (Deleted) -34.29kB 0 bytes -100.0% 🗑️
assets/owner-panel-B9vXIdGL.js (Deleted) -26.89kB 0 bytes -100.0% 🗑️
assets/app-D-HJtywB.js (Deleted) -25.65kB 0 bytes -100.0% 🗑️
assets/app.runs-Cu-ZedLg.js (Deleted) -23.55kB 0 bytes -100.0% 🗑️
assets/miner-panel-C-RiwyO0.js (Deleted) -20.25kB 0 bytes -100.0% 🗑️
assets/docs-source-B94HAFfX.js (Deleted) -19.3kB 0 bytes -100.0% 🗑️
assets/api._op-Dw8bqWur.js (Deleted) -17.82kB 0 bytes -100.0% 🗑️
assets/self-hosting-docs-audit-DzFBxSXL.js (Deleted) -16.73kB 0 bytes -100.0% 🗑️
assets/app.index-CAqwkuq_.js (Deleted) -15.62kB 0 bytes -100.0% 🗑️
assets/playground-panel-CeP8ddqP.js (Deleted) -14.35kB 0 bytes -100.0% 🗑️
assets/extension-MsiAELNv.js (Deleted) -10.04kB 0 bytes -100.0% 🗑️
assets/app.config-generator-Di-xYCLX.js (Deleted) -9.86kB 0 bytes -100.0% 🗑️
assets/app.audit-DDV74huK.js (Deleted) -9.54kB 0 bytes -100.0% 🗑️
assets/maintainers-BeH4CM4g.js (Deleted) -8.06kB 0 bytes -100.0% 🗑️
assets/miners-DmRE7dJ6.js (Deleted) -7.89kB 0 bytes -100.0% 🗑️
assets/agents-BOnYd7NY.js (Deleted) -7.76kB 0 bytes -100.0% 🗑️
assets/commands-panel-BVg3HRXx.js (Deleted) -6.63kB 0 bytes -100.0% 🗑️
assets/roadmap-B7XuHt2f.js (Deleted) -6.33kB 0 bytes -100.0% 🗑️
assets/repos._owner._repo.quality-CcNKzwlJ.js (Deleted) -6.14kB 0 bytes -100.0% 🗑️
assets/digest-panel-CnCAOhSi.js (Deleted) -6.14kB 0 bytes -100.0% 🗑️
assets/docs.index-Bt9X11LK.js (Deleted) -5.42kB 0 bytes -100.0% 🗑️
assets/api.index-Bues_foC.js (Deleted) -4.74kB 0 bytes -100.0% 🗑️
assets/api-7UMgYxG1.js (Deleted) -2.69kB 0 bytes -100.0% 🗑️
assets/docs-Dlg9wSXt.js (Deleted) -2.67kB 0 bytes -100.0% 🗑️
assets/app.workbench-BHEHmYNZ.js (Deleted) -1.58kB 0 bytes -100.0% 🗑️
assets/tooltip-BPJ1_Qkn.js (Deleted) -1.45kB 0 bytes -100.0% 🗑️
assets/session-rOwegFOp.js (Deleted) -1.44kB 0 bytes -100.0% 🗑️
assets/tabs-C4vT7TEd.js (Deleted) -1.39kB 0 bytes -100.0% 🗑️
assets/app.repos-WqcGbrjA.js (Deleted) -1.07kB 0 bytes -100.0% 🗑️
assets/input-BqD6XjfD.js (Deleted) -796 bytes 0 bytes -100.0% 🗑️
assets/file-cog-DMtrgloC.js (Deleted) -758 bytes 0 bytes -100.0% 🗑️
assets/docs.ai-summaries-xPRPTK2f.js (Deleted) -506 bytes 0 bytes -100.0% 🗑️
assets/app.maintainer--u0WZXbu.js (Deleted) -502 bytes 0 bytes -100.0% 🗑️
assets/docs.branch-analysis-BlKdBrdz.js (Deleted) -499 bytes 0 bytes -100.0% 🗑️
assets/docs.scoreability-rlrEqIyN.js (Deleted) -499 bytes 0 bytes -100.0% 🗑️
assets/docs.upstream-drift-C42RaAmy.js (Deleted) -499 bytes 0 bytes -100.0% 🗑️
assets/docs.federated-fleet-intelligence-CDunbCGq.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.maintainer-install-trust-DkIKV97Q.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-ai-providers-Cm0LXBE4.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-backup-scaling-B_RlzU_U.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-configuration-BV0YZVCw.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-docs-audit-B80Kml6o.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-github-app-DUzETraP.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-quickstart-CMCBFkOq.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-rag-Dg18joZf.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-rees-DzrF6vR-.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-rees-analyzers-DPSsc4kq.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-release-checklist-f6ldsG9y.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-releases-DoIO-zHb.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-security-C-LE6y_L.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-troubleshooting-CTqAWmg6.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.self-hosting-unified-ams-orb-B54rPSYy.js (Deleted) -498 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-config-precedence-u6j1tSC7.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-deployment-D4__lcAD.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-discovery-plane-B1o5ePl3.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-env-reference-CNVxy7HN.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-fleet-manifest-CsXTgJtf.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-goal-spec-Dg5lrS1x.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-observability-0hRaewA0.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-operations-runbook-CMqGNul9.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-sizing-C8YkSfh3.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.ams-unattended-scheduling-C5ZuSQ-9.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.beta-onboarding-t-EM4RPK.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.maintainer-self-hosting-aHb7aE-W.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.mcp-clients-B5Ym3x_a.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.owner-checklist-B4bCnQVb.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.quickstart---eiyvUo.js (Deleted) -497 bytes 0 bytes -100.0% 🗑️
assets/docs.github-app-DRe9EB6B.js (Deleted) -495 bytes 0 bytes -100.0% 🗑️
assets/docs.maintainer-workflow-BoqRyCDy.js (Deleted) -495 bytes 0 bytes -100.0% 🗑️
assets/docs.privacy-security-DjhM9NzX.js (Deleted) -495 bytes 0 bytes -100.0% 🗑️
assets/docs.troubleshooting-DNX0LLfH.js (Deleted) -495 bytes 0 bytes -100.0% 🗑️
assets/docs.tuning-Df1xP-mJ.js (Deleted) -495 bytes 0 bytes -100.0% 🗑️
assets/docs.loopover-commands-JDwrNYos.js (Deleted) -494 bytes 0 bytes -100.0% 🗑️
assets/sparkles-BjE6Fq61.js (Deleted) -494 bytes 0 bytes -100.0% 🗑️
assets/docs.how-reviews-work-DzWoBG6-.js (Deleted) -493 bytes 0 bytes -100.0% 🗑️
assets/app.owner-r6P1QTrY.js (Deleted) -474 bytes 0 bytes -100.0% 🗑️
assets/app.commands-0kWs4tu6.js (Deleted) -455 bytes 0 bytes -100.0% 🗑️
assets/app.playground-BOyiWl6q.js (Deleted) -442 bytes 0 bytes -100.0% 🗑️
assets/reveal-Bzo44a0X.js (Deleted) -442 bytes 0 bytes -100.0% 🗑️
assets/index-CFar3Wjb.js (Deleted) -438 bytes 0 bytes -100.0% 🗑️
assets/app.digest-DqFXDR0-.js (Deleted) -430 bytes 0 bytes -100.0% 🗑️
assets/eye-off-BPp31W8O.js (Deleted) -430 bytes 0 bytes -100.0% 🗑️
assets/app.miner-Bi3XyPZq.js (Deleted) -422 bytes 0 bytes -100.0% 🗑️
assets/key-round-DLW4zHnZ.js (Deleted) -355 bytes 0 bytes -100.0% 🗑️
assets/bot-RrvZxneY.js (Deleted) -328 bytes 0 bytes -100.0% 🗑️
assets/trash-2-YBNZoQRr.js (Deleted) -328 bytes 0 bytes -100.0% 🗑️
assets/save-CZCAq_Go.js (Deleted) -327 bytes 0 bytes -100.0% 🗑️
assets/git-pull-request-arrow-CiBhYJ57.js (Deleted) -321 bytes 0 bytes -100.0% 🗑️
assets/shield-check-MojloxrF.js (Deleted) -320 bytes 0 bytes -100.0% 🗑️
assets/wrench-BHzuEqrU.js (Deleted) -303 bytes 0 bytes -100.0% 🗑️
assets/list-checks-tZVHSd4z.js (Deleted) -279 bytes 0 bytes -100.0% 🗑️
assets/shield-DFEjKEnx.js (Deleted) -272 bytes 0 bytes -100.0% 🗑️
assets/workflow-WIKFpq8w.js (Deleted) -265 bytes 0 bytes -100.0% 🗑️
assets/compass-zg0giQH9.js (Deleted) -251 bytes 0 bytes -100.0% 🗑️
assets/history-BB1LlylR.js (Deleted) -237 bytes 0 bytes -100.0% 🗑️
assets/activity-DkUD_1gs.js (Deleted) -234 bytes 0 bytes -100.0% 🗑️
assets/message-square-Cps1JaJO.js (Deleted) -233 bytes 0 bytes -100.0% 🗑️
assets/lock-C5IpBYRc.js (Deleted) -206 bytes 0 bytes -100.0% 🗑️
assets/rotate-cw-BlNp4Ly5.js (Deleted) -201 bytes 0 bytes -100.0% 🗑️
assets/play-BLOhYeHO.js (Deleted) -190 bytes 0 bytes -100.0% 🗑️
assets/circle-check-DJ_9o-uF.js (Deleted) -178 bytes 0 bytes -100.0% 🗑️
assets/search-h9srosrw.js (Deleted) -174 bytes 0 bytes -100.0% 🗑️
assets/self-hosting-docs-audit-DZI6xic3.js (Deleted) -141 bytes 0 bytes -100.0% 🗑️
assets/circle-D6FQgrQy.js (Deleted) -130 bytes 0 bytes -100.0% 🗑️
assets/modal-PDoXifdW.js (Deleted) -115 bytes 0 bytes -100.0% 🗑️

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.77%. Comparing base (46b8731) to head (c5cc0e5).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7201   +/-   ##
=======================================
  Coverage   93.77%   93.77%           
=======================================
  Files         695      695           
  Lines       69042    69044    +2     
  Branches    18811    18811           
=======================================
+ Hits        64746    64748    +2     
  Misses       3302     3302           
  Partials      994      994           
Flag Coverage Δ
shard-1 43.46% <100.00%> (-0.45%) ⬇️
shard-2 37.20% <0.00%> (+0.06%) ⬆️
shard-3 33.17% <100.00%> (+0.01%) ⬆️
shard-4 32.64% <0.00%> (-1.87%) ⬇️
shard-5 32.73% <0.00%> (+0.53%) ⬆️
shard-6 46.11% <100.00%> (+0.31%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/db/repositories.ts 96.71% <100.00%> (+<0.01%) ⬆️
src/services/operator-dashboard.ts 100.00% <ø> (ø)

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 18, 2026
@loopover-orb

loopover-orb Bot commented Jul 18, 2026

Copy link
Copy Markdown

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-18 12:19:37 UTC

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

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This PR adds a per-tenant row-count breakdown of ai_usage_events mirroring the existing listAiCostByTenantSince pattern from #7191, wired through the dashboard payload into a new UI section following the same conditional-render shape as its sibling. The query, null/window filtering, and ordering are correct and directly tested against real SQLite via createTestEnv, and the UI tests cover absent/empty/populated states. The linked issue #4890 is only partially addressed per the brief (scoped to capacity planning broadly, narrowed here to row-count-only), which is acknowledged in the description as a re-scoping rather than full closure.

Nits — 6 non-blocking
  • The `/* v8 ignore next */` comment in src/db/repositories.ts justifies skipping coverage on the `row.installationId ?? ""` fallback — reasonable given the isNotNull filter, but worth double-checking this pattern isn't silently masking a real driver quirk if D1's typing ever changes.
  • apps/loopover-ui/src/routes/app.operator.tsx and src/db/repositories.ts are already large files (per the brief, ~540 and ~3673 lines) — this PR adds another parallel block to both rather than extracting a shared per-tenant-breakdown helper, compounding the growth pattern from feat(api): surface per-tenant AI cost on the operator dashboard #7191.
  • The dashboard test's date `2026-07-17T00:00:00.000Z` as `loadedAt` and the seed timestamps in ai-usage-tenant.test.ts hardcode 2026 dates, consistent with existing test conventions but worth a comment noting why (avoids drift confusion later).
  • Consider extracting a small shared helper (e.g. `groupCountByTenant`) if a third per-tenant metric is added later, to avoid a third near-identical block in repositories.ts.
  • Confirm the linked issue Add tenant-aware capacity planning for the shared database #4890's broader 'capacity planning' scope is tracked separately if this PR only closes the row-count dimension, so the issue isn't prematurely closed if reviewers expect full closure.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #4890
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: 31 registered-repo PR(s), 24 merged, 320 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 31 PR(s), 320 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Addressed
The PR delivers per-tenant storage visibility (row counts by installationId, surfaced on the operator dashboard) and explicitly builds on the already-shipped account-level D1 alerting from the retention-fix incident, matching the issue's core deliverable; the PR description also notes the issue was re-scoped by an earlier narrowing comment to this specific per-tenant-visibility gap.

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: not available
  • Official Gittensor activity: 31 PR(s), 320 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Treat this as maintainer-lane context rather than normal contributor-lane activity.
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.
🧪 Chat with LoopOver

Ask 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.

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

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

Visual preview
Route Viewport Before (production) After (this PR's preview) Diff
/app/operator/test desktop before /app/operator/test
before /app/operator/test
after /app/operator/test
after /app/operator/test
/app/operator/test mobile before /app/operator/test (mobile)
before /app/operator/test (mobile)
after /app/operator/test (mobile)
after /app/operator/test (mobile)
/app/operator desktop before /app/operator
before /app/operator
after /app/operator
after /app/operator
/app/operator mobile before /app/operator (mobile)
before /app/operator (mobile)
after /app/operator (mobile)
after /app/operator (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

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoopOver approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit cc3a201 into main Jul 18, 2026
20 checks passed
@loopover-orb
loopover-orb Bot deleted the worktree-ai-usage-storage-by-tenant branch July 18, 2026 12:19
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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add tenant-aware capacity planning for the shared database

1 participant