KDCube

A paid surface is any place an application reaches a paid service: a chat turn, a standalone search box, a REST operation, a background job, a cron routine. The chat entrypoint already runs turns under the economics model. Everything else — the accountable work that runs on a user's behalf without a chat turn — used to be on its own. Now it runs under the same model through one small enforcement engine.

The model has one shape everywhere: verify, reserve, run, settle. Verify that the user's quota and funding can cover an estimate; reserve that estimate so concurrent requests cannot oversubscribe it; run the work; settle the actual cost across the funding sources. Deny before anything runs, and no money hold is left behind.

VERIFY · RESERVE · RUN · SETTLE Verify resolve plan & funding admit against quota Reserve hold the estimate concurrency-safe Run accounted work usage emitted into scope Settle actual cost, same split commit / release holds deny — nothing ran, no hold left behind quota / funding reservation EconomicsLimitException raises before any work runs; a zero-cost flow releases its holds.
Verify → reserve → run → settle; denial raises before any work runs, with no hold left behind.

One request is one funding split

Before it can reserve anything, the engine resolves who pays. Every request is a single split. A primary funding source — an external subscription's period budget, or the project budget for everyone else — covers the part bounded by both the remaining plan quota and the primary funds. The user's wallet covers the remainder.

R = estimated cost
plan_part   = min(R, Q, P)      # Q = remaining plan quota, P = primary funds
wallet_part = R - plan_part     # the over-quota / over-funds remainder

When the plan quota or funds run out, plan_part simply shrinks toward zero and the wallet covers the rest in the same pass. The request only denies when there is no wallet, or the wallet cannot cover the remainder. At settlement the same order holds — plan quota and primary funds first, wallet for the over-quota remainder — and if the actual spend still overshoots, the project budget absorbs the residual as a last resort. Subscriptions and wallets never go negative.

ONE REQUEST, ONE FUNDING SPLIT R = estimated cost plan_part min(R, Q, P) primary: subscription / project · under quota Q, funds P wallet_part R − plan_part wallet covers the over-quota remainder if actual spend still overshoots → the project budget absorbs the residual (last resort) When plan quota or funds run out, plan_part shrinks toward zero and the wallet covers the rest in the same pass. Subscriptions and wallets never go negative.
One request, one split — the primary source under quota, the wallet for the remainder, the project budget for any residual.

This is the part your application does not want to rewrite, and does not have to. It resolves once, in the engine, from the request's economics subject.

Two entry points, chosen by who settles

The engine offers exactly two ways in, and the choice is about who settles the cost. EconomicsGuard is an async context manager around one accountable flow. On enter it resolves plan and funding, admits against quota, reserves the estimate, and binds accounting to the flow. On exit it reads back the flow's usage and settles the actual cost — committing or releasing the reservation.

async with EconomicsGuard(
    self, subject=subject,
    scope_id="report_render_42", flow="reports.render",
    estimate=EconomicsEstimate(reservation_usd=0.05),
) as decision:
    result = await do_the_paid_work()      # accounted calls run here

economic_preflight is the same admit and funding resolution with no reservation and no settlement — a feasibility gate for when the cost is metered elsewhere, or the caller degrades gracefully on denial.

try:
    await economic_preflight(self, subject=subject,
                             estimate=EconomicsEstimate(reservation_usd=0.01),
                             flow="reports.preview")
except EconomicsLimitException:
    use_cheaper_path()

Both raise EconomicsLimitException before any work runs. The code says why — rate_limited, no_funding_source, quota_lock_timeout — and the surface picks its reaction.

Search degrades instead of failing

The most common paid surface is semantic search, and it has a purpose-built facade so components never touch the guard. An economics-enabled entrypoint hands a searchable component one dependency:

model_service = self.search_model_service(flow="reports.search", subject=subject)

Two methods, deliberately different in how they fail.

  • embed_texts([...]) embeds document/index material and lets exceptions propagate — a write must not silently skip content.
  • embed_search_query(query, flow=...) wraps the query embed in a guard and returns None on economics denial, so the query layer falls back to lexical ranking (BM25, keyword) instead of failing the request.

Search under economics does not break — it gets cheaper. Memory search, canvas pin search, and task search all degrade this way.

MANY SURFACES · ONE MODEL chat turn settles its own turn standalone search UI / API surface background job detached / cron Enforcement engine guard · preflight verify · reserve · settle Economics model plans · wallets · quota one funding split · settlement nested in a turn → verify-only A guard nested inside a chat turn degrades to verify-only — the turn settles. Outside a turn, it settles its own operation scope.
Every surface enters one engine over one model; a guard nested inside a chat turn degrades to verify-only.

The same work is never charged twice

A chat turn marks itself as the active parent economics scope while it runs the bundle core. A guard — or the search facade — entered inside that scope automatically degrades to verify-only: it checks feasibility but leaves the usage event in the parent turn, and the turn settles it. A guard outside any parent scope creates and settles its own operation scope. You do not wire this; a context variable carries it. The rule is simply that one unit of work settles once.

What your application actually writes

Becoming economics-enabled is extending one base (BaseEntrypointWithEconomics), which binds the runtime primitives the engine reuses. From there, guarding a paid surface is three decisions: resolve the subject (who pays), size the estimate (a few cents for most non-chat flows), pick EconomicsGuard or economic_preflight. The plans, wallets, quota windows, reservations, shortfall absorption, and settlement math stay in the engine.

And when the paid work is your own — not an LLM, embedding, or search, but a metered API you call — you make it countable with a tracker so the guard has a real cost to settle. That is a short, separate move, covered in the companion journal note and recipe. The alternate runtimes ride the same rails: a Claude Code turn is accounted as ordinary LLM usage — same tracker, provider="anthropic", a self-reported per-turn cost — priced right beside the built-in ReAct runtime's calls. The result is a single answer to "may this run, and who pays?" that holds whether the paid call sits in a chat turn, a search box, a REST handler, or a job — and an application that spends a user's money only when the platform has agreed it can.

KDCube Deep · 13.07.2026