Skip to content

feat(engine): per-loop compute consumption ledger entries - #6126

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
luciferlive112116:feat/loop-consumption-ledger-entry
Jul 15, 2026
Merged

feat(engine): per-loop compute consumption ledger entries#6126
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
luciferlive112116:feat/loop-consumption-ledger-entry

Conversation

@luciferlive112116

Copy link
Copy Markdown
Contributor

Summary

Closes #4792

Rent-a-Loop has no mechanism recording what a rented loop actually consumed against what was paid or staked. #4796 landed the downstream half — evaluateTenantQuota reads a tenant's computeUnitsUsed/wallClockMsUsed — but nothing produces those numbers from a real run.

This adds the upstream pure decision core, mirroring tenant-quota.ts's shape and normalization discipline exactly:

  • buildLoopConsumptionEntry(facts) — given one finished loop run's already-metered facts, produces the consumption entry a rental ledger records: the tenant it bills to, the wall-clock it occupied, the compute it burned.
  • totalConsumptionForTenant(entries, tenantId) — sums a period's entries into exactly the TenantUsage shape evaluateTenantQuota reads, so consumption is queryable against allocation (the issue's deliverable). A test asserts that composition end-to-end.

Both acceptance criteria:

  • A completed test loop produces a ledger entry with accurate elapsed compute/time.
  • A killed-mid-run test loop also produces an accurate, consistent ledger entry — it bills identically for what it consumed, flagged complete: false rather than dropped. A killed run really did occupy the compute, so it is a first-class case, not an error path.

Scope — what this deliberately does not do

It computes an entry only: no ledger write, no metering, no pricing. That is the line the issue draws, and it is respected:

This is the same pure-decision-core shape the sibling Rent-a-Loop issues merged as: #5801 (tenant-quota.ts#4796), and #4801/#4800/#4806's own evaluators.

  • Conventional Commit title (feat(engine): …).
  • Focused: one new pure module + its barrel export + its test. No existing behavior changed.
  • Follows CONTRIBUTING.md; no site//CNAME/lovable; no changelog edit.
  • Linked open issue (Closes Rental ledger integration #4792, above).

Validation

  • git diff --check clean.
  • npm run build:miner — exit 0 (the engine package compiles + emits).
  • npm run typecheck — exit 0.
  • npx vitest run test/unit/loop-consumption.test.ts test/unit/tenant-quota.test.ts22 tests passed (the sibling suite is included to prove the composition still holds).
  • Coverage on packages/loopover-engine/src/loop-consumption.ts (in Codecov's coverage.include): 100% statements (12/12), 100% branch (4/4), 100% functions (3/3), 100% lines (11/11).
  • Rebased onto current main.

Invariants a billing ledger must not get wrong — each tested:

Invariant Why it matters
An end before its start floors at 0 Clock skew, or a kill recorded against a stale start, must never produce a negative charge
Non-finite / negative / fractional readings normalize A ledger billing NaN or -1 ms is worse than one billing 0
Compute is never inferred from elapsed time An idle loop and a saturated one of the same duration did not consume the same compute; guessing bills for work that never happened
A tenant is never billed for another tenant's entries The one mistake a rental ledger must never make — so the caller's filtering is not trusted
activeLoops is not derived A finished run says nothing about what is running now; fabricating it would make evaluateTenantQuota's concurrency dimension decide on an invented number

Naming note

LoopConsumptionOutcome is deliberately its own type rather than reusing loop-escalation.ts's LoopRunOutcome, which describes a loop's health state (running/converged/abandoned/error). A consumption entry only exists for a run that has already stopped, and only distinguishes finished work from work cut short — reusing the name would collide in the barrel and conflate two different domains.

Safety

  • No secrets, wallets, hotkeys, trust scores, rewards, private rankings, or private maintainer evidence. No compensation/payout value is computed or embedded — this measures consumption; pricing is explicitly out of scope.
  • No auth/cookie/CORS/GitHub App/session change.
  • Additive and inert: a new module plus its barrel export. Nothing imports it yet, so runtime behavior is byte-identical until the (blocked) integration wires it up.
  • Pure and side-effect-free: no IO, no storage, no clock read (timestamps are inputs, never Date.now()), so it is deterministic and trivially testable.
  • Multi-tenant safe by construction: it reads only the tenant it is handed and filters by tenantId itself, so one tenant's entries can never leak into another's total.
  • No API/OpenAPI/MCP change; no schema change; no generated artifact affected.
  • No UI changes; no changelog edit.

Rent-a-Loop has no mechanism recording what a rented loop actually
consumed against what was paid or staked (JSONbored#4792). JSONbored#4796 landed the
downstream half -- evaluateTenantQuota reads a tenant's
computeUnitsUsed/wallClockMsUsed -- but nothing produces those numbers
from a real run.

Add the pure decision core for the upstream half: given one finished
loop run's already-metered facts, buildLoopConsumptionEntry produces the
consumption entry a rental ledger records, and totalConsumptionForTenant
sums a period's entries into exactly the TenantUsage shape
evaluateTenantQuota reads -- so consumption is queryable against
allocation, which is what the issue asks for.

Both acceptance criteria are covered: a completed run bills its real
elapsed wall-clock and metered compute, and a killed-mid-run run bills
identically for what it consumed, flagged incomplete rather than dropped
-- it really did occupy the compute.

Deliberately pure, holding the line the issue draws. It computes an
entry only: no ledger write, no metering, no pricing. Persisting is the
integration blocked on JSONbored#4789/JSONbored#4790, and per JSONbored#5669 must target whatever
storage abstraction JSONbored#4940/JSONbored#5216 lands on -- so this core has no storage
opinion and stays correct whichever that is.

Invariants a billing ledger must not get wrong, all tested: an
end-before-start timestamp (clock skew, or a kill against a stale start)
floors at 0 rather than a negative charge; non-finite/negative/fractional
readings normalize instead of producing NaN; compute is never inferred
from elapsed time, since an idle loop and a saturated one of the same
duration did not consume the same compute; and a tenant is never billed
for another tenant's entries.

LoopConsumptionOutcome is its own type rather than reusing
loop-escalation.ts's LoopRunOutcome, which is a loop's health state
(running/converged/abandoned/error) -- a consumption entry only exists
for a run that already stopped.

Closes JSONbored#4792
@superagent-security

Copy link
Copy Markdown
Contributor

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

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.31%. Comparing base (42695b3) to head (40a6920).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6126   +/-   ##
=======================================
  Coverage   95.31%   95.31%           
=======================================
  Files         595      596    +1     
  Lines       47100    47111   +11     
  Branches    15032    15034    +2     
=======================================
+ Hits        44893    44904   +11     
  Misses       1476     1476           
  Partials      731      731           
Flag Coverage Δ
shard-1 43.98% <0.00%> (-0.02%) ⬇️
shard-2 36.63% <100.00%> (-0.01%) ⬇️
shard-3 32.13% <0.00%> (+<0.01%) ⬆️
shard-4 33.44% <0.00%> (-0.01%) ⬇️
shard-5 31.47% <0.00%> (-0.01%) ⬇️
shard-6 44.83% <0.00%> (-0.02%) ⬇️

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

Files with missing lines Coverage Δ
packages/loopover-engine/src/loop-consumption.ts 100.00% <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 15, 2026
@loopover-orb

loopover-orb Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-15 11:14:23 UTC

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

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This adds a pure, side-effect-free module `loop-consumption.ts` that builds a per-loop billing entry from metered facts and sums entries into the exact `TenantUsage` shape `evaluateTenantQuota` consumes, plus re-exports and a thorough test suite covering normalization, kill-mid-run billing, and end-to-end composition with the existing quota evaluator. The logic is correct: timestamps and compute are normalized via `finiteNonNegativeInt` before subtraction/summation, so negative/NaN/skewed inputs can't produce a negative or NaN charge, and the killed-run case is handled as a first-class, same-shape entry with `complete: false`. Scope is honored — no ledger write, no storage opinion — matching the stated deliverable of #4792, and the composition test with `evaluateTenantQuota` demonstrates the acceptance criterion is actually met rather than merely asserted.

Nits — 5 non-blocking
  • The doc comments in loop-consumption.ts are unusually long multi-paragraph blocks (e.g. the file header and function docstrings) — consider trimming to the non-obvious rationale only, per repo comment conventions.
  • `computeUnitsMetered: 0` case (an unmetered run billing 0 compute) is tested but relies on caller discipline to always meter; consider a brief note in the type doc that 0 is ambiguous between 'unmetered' and 'genuinely idle'.
  • `totalConsumptionForTenant` re-normalizes already-normalized entry fields (loop-consumption.ts computeUnits/wallClockMs) — defensive but slightly redundant with `buildLoopConsumptionEntry`'s guarantees; fine as a boundary safeguard against corrupt stored entries, just note why in a short comment.
  • Since this is a pure decision core with no persistence yet, consider adding a one-line JSDoc pointer from `LoopRunFacts` to the future producer (Neuron/hotkey registration flow #4789/Stake/payment → compute-time pricing #4790) so future readers know where the real timestamps/metering will come from.
  • Nothing else to add — the diff is narrow, well-tested, and matches its stated scope.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #4792
Related work ⚠️ 3 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High 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: 120 registered-repo PR(s), 62 merged, 35 issue(s).
Contributor context ✅ Confirmed Gittensor contributor luciferlive112116; Gittensor profile; 120 PR(s), 35 issue(s).
Gate result ✅ Passing No configured blocker found.
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Partially addressed
The PR delivers a well-tested pure function producing accurate consumption entries for both completed and killed runs, satisfying the two acceptance criteria at the unit level, but it explicitly does not write to any ledger (run-state/claim/portfolio-queue/event) or produce a queryable persisted entry, which the issue's deliverable requires ('ledger entries recording consumption... rather than a p

Review context
  • Author: luciferlive112116
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, JavaScript, MDX, Rust, TypeScript
  • Official Gittensor activity: 120 PR(s), 35 issue(s).
  • Related work: Items reference the same linked issue Rental ledger integration #4792. (issue #4792)
  • Related work: Open PR work references issue Rental ledger integration #4792. (issue #4792)
  • Related work: Titles/paths share 3 meaningful terms. (issue #4792, issue #4939)
Contributor next steps
  • Start here: Review top overlaps.
  • Then work through the remaining 2 steps in the Signals table above.
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://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.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

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 fcc897d into JSONbored:main Jul 15, 2026
16 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rental ledger integration

1 participant