Skip to content

feat(budget): enforce the lifetime cap in the chat request path [3/3] - #170

Closed
hasitpbhatt wants to merge 32 commits into
Continuum-AI-Corp:mainfrom
hasitpbhatt:budget/enforce
Closed

hasitpbhatt wants to merge 32 commits into
Continuum-AI-Corp:mainfrom
hasitpbhatt:budget/enforce

Conversation

@hasitpbhatt

@hasitpbhatt hasitpbhatt commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Orca-Code-Review — push 1

Severity Count
P0 0
P1 1
P2 0
P3 0

❌ 1 finding blocks merge

Review this diff, not the whole PR: budget/models..budget/enforce
(git diff budget/models..budget/enforce). The rest of this branch is layers 1
and 2, which are PRs #168 and #169 and reviewed separately. The PR has to
target main because the intermediate branches cannot be pushed to this
repository.

Wires ApiKey.budget_limit_cents into execute_chat: a capped key at its cap is
refused with 429 before dispatch, and a request that is served leaves the counter
moved by exactly what it cost, in the same transaction as its request-log row.

What lands here

  • The pre-check runs after every validation that can reject a request, so a
    request refused before touching an upstream consumes no budget.
  • Streaming and blocking paths settle identically: the row and the charge are one
    commit, and a persisted trace_id proves the charge also landed, so a retry
    never double-charges.
  • Unmeasured deliveries are priced from what actually reached the client
    (app.budget_pricing), with the ending decided in one place
    (_unmeasured_charge) so no handler can price its own ending differently from
    its siblings.
  • A settlement that cannot be recorded after every retry parks the obligation
    against the cap rather than dropping it.
  • Folded traces leave tombstones (budget_folded), so a memory hold that
    outlives another worker's fold is dropped at re-file instead of billed twice.

Fixes included

  1. Cancellation swallowed after a late commit. The commit-cancellation arms
    returned normally once the detached commit succeeded, discarding the
    CancelledError. They now fall through to the re-raise.
  2. Known-free streams charged the whole remaining cap. The completed-without-
    usage branch charged cap - spent for every model. It now charges zero only
    when the catalog entry is priced 0.0 in and 0.0 out.
  3. A cancelled blocking commit dropped the charge. It now parks before
    propagating, and the durability probe runs to a decision even under a second
    cancel.
  4. Streaming measured-but-unpriceable settled at zero. The streaming
    settlement returned the row's 0 cost untouched for countable tokens on a
    custom/unlisted model. It now mirrors the blocking twin's 1-microcent
    fail-closed floor.
  5. Cross-worker double-charge. A hold coexisting with its durable row (landed
    commit + lost ack + failed probe) was re-filed after another worker folded
    it: spent 100 → 200 for one obligation. Folds now tombstone fully-billed
    traces in the same transaction; re-filing a tombstoned hold drops it, a row
    re-filed in the race window is deleted unbilled, and pending spend excludes
    both. The old "fail-closed double-count" trade-off below no longer applies.

Trade-offs accepted deliberately

  • Measured cost can be raised to the estimate on a no-usage path
    (max(actual, charge)). Deliberate never-under-charge.
  • budget_limit_cents=0 rejects every request, including free models.
    Negative behaves the same; absent means uncapped. Pinned by tests.
  • Non-text pricing floor is approximate. An image/audio part counts one
    character (one token), not its provider-side cost, which is unknowable here.
    Counting base64 bytes would overcharge far more. Documented in
    app/budget_pricing.py.

Review state

OrcaCode Review (local) at a2b7b62: passed, no findings. All review
threads resolved (0 open).

Verification

  • Full suite on budget/enforce: 914 passing, ruff clean — including
    settlement-matrix tests (zero usage with/without delivery, pre-delivery
    failure, zero/negative/absent caps, every _unmeasured_charge ending),
    tombstone regression tests, blocking/streaming parity tests, and
    cancellation boundary tests.
  • Runs with greenlet able to load libstdc++; CI should not need the local workaround.
  • CI (tests, package, docker smoke) green on the head.

Part of the budget stack tracked in #165. Merge order: helpers → models → enforce.

@orcacode-review orcacode-review 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.

🐳 OrcaCode Review

Found 1 issue in this PR: 🟠 1 P1.

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.
Engine-reported: 251 calls · 14.1M tokens · 95% cached

❤️ Share · Install OrcaCode Review

Free on GitHub — the review runs on your own OrcaRouter key. If it helped, a shout-out goes a long way.

Share: X · Reddit · LinkedIn
Follow: X · Discord · LinkedIn · OrcaRouter

Comment thread packages/auth/spend.py

@orcacode-review orcacode-review 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.

🐳 OrcaCode Review

Found 2 issues in this PR: 🟠 2 P1.

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.
Engine-reported: 240 calls · 14.5M tokens · 95% cached

❤️ Share · Install OrcaCode Review

Free on GitHub — the review runs on your own OrcaRouter key. If it helped, a shout-out goes a long way.

Share: X · Reddit · LinkedIn
Follow: X · Discord · LinkedIn · OrcaRouter

Comment thread app/routes/chat.py
Comment thread packages/auth/spend.py Outdated

@orcacode-review orcacode-review 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.

⚠️ Outdated review — the PR head changed from b763f38 to 0857a86 during the review; these findings describe a diff the PR no longer has.
These findings describe b763f38e51ce. Re-run the review to check the PR as it stands now.
The gate evaluated to blocked, but it is not enforced here: branch protection will not see it.

🐳 OrcaCode Review

Found 1 issue in this PR: 🟠 1 P1.

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.
Engine-reported: 257 calls · 15.5M tokens · 95% cached

❤️ Share · Install OrcaCode Review

Free on GitHub — the review runs on your own OrcaRouter key. If it helped, a shout-out goes a long way.

Share: X · Reddit · LinkedIn
Follow: X · Discord · LinkedIn · OrcaRouter

Comment thread app/routes/chat.py

@orcacode-review orcacode-review 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.

🐳 OrcaCode Review

Found 2 issues in this PR: 🟠 2 P1.

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.
Engine-reported: 244 calls · 14.5M tokens · 95% cached

❤️ Share · Install OrcaCode Review

Free on GitHub — the review runs on your own OrcaRouter key. If it helped, a shout-out goes a long way.

Share: X · Reddit · LinkedIn
Follow: X · Discord · LinkedIn · OrcaRouter

Comment thread app/routes/chat.py
Comment thread packages/auth/spend.py Outdated
Characters-to-tokens estimation and usage detection for requests whose
upstream never reported a measurement. No database or route state, so
the pricing rules can be tested in isolation before settlement uses them.
`countable_usage` accepted any countable key that was merely present, so a
usage frame of `{"prompt_tokens": 0, "completion_tokens": 0}` — what LiteLLM
emits when the upstream reported nothing — counted as measured. Settling on it
records a zero cost for a delivered completion, which is exactly the bypass the
guard exists to prevent.

Only a countable key holding a positive number is now a measurement. A zero on
one side still counts when the other side is positive, so a prompt-only request
is unaffected. `bool` is rejected explicitly because `True` is an `int`.

A genuinely empty completion still prices at zero through
`blocking_delivery_chars` and `estimate_usage`, so this cannot overcharge.
text_chars only counted dict parts ({"text": "..."}), so an Anthropic-style
prompt of bare strings (`["hello"]`) priced at 0 characters. blocking_delivery_chars
counts the same shape on a completion, so a budgeted key's prompt was
under-priced whenever the wire used plain-string parts — the precondition is
/v1/messages forwarding Anthropic-style content, reachable by real clients.

Count plain string parts in text_chars the way blocking_delivery_chars does,
keeping the dict branch, so a prompt and a completion of the same content
price identical characters.

Verified: text_chars(["hello","world"]) == 10; a mixed list prices only its
textual parts.
Regression for the bare-string pricing fix: a list of plain strings
(`["hello", "world"]`) and a mixed list with bare-string parts now have
expected character counts. Mirrors blocking_delivery_chars, which already
accepted str parts, so prompt and completion price the same characters.
…t on delivery, count tool_use parts

- message_chars: prompt side counts tool_calls as well as content, matching
  the delivery path, so tool-call-only assistant turns no longer price at zero
- countable_usage(delivered=True): a delivered completion needs a positive
  completion-side count; a prompt-only frame no longer settles it at zero
- _part_texts: Anthropic tool_use parts count name and string input, shared
  by text_chars and blocking_delivery_chars so the two cannot drift
… with a test

text_chars intentionally counts whitespace; blocking_delivery_chars skips it
only for the delivered-or-not decision. Streamed deltas are priced through
text_chars, so dropping whitespace there would undercount real output.
…vered opaque parts

- tool_use: count json.dumps(input or {}), the same serialization the
  Anthropic and Gemini translators bill, instead of dropping object input
- delivery: count message.refusal, which reaches the client as output
- delivery: a non-text delivered part (image, audio) counts as one character
  so it floors to one token and never settles at zero; the prompt-side
  text_chars still counts it exactly
A non-text prompt part (image, tool_result) was billed but counted zero, so an
image-only prompt floored to zero tokens. Such a part now counts one character,
so it floors to one token. Both prompt and delivery paths share _part_texts.
…racter

A tool_result's billable text is its content (string or text parts). It was
priced as a single opaque character, undercharging multi-turn tool prompts
by the length of every tool output. Its text is now counted; nested non-text
parts still floor to one character.
@orcacode-review

Copy link
Copy Markdown

🐳 OrcaCode Review

This pull request was not reviewed.

A maintainer of this repository can comment @orcacode review to run it again. If it keeps failing, the usual causes are the account running out of credit or the app losing access to this repository — both are visible in the OrcaRouter settings. If neither applies, contact OrcaRouter support.

No comments were posted for this commit.

Details
Code ORCA-RELAY
Stage review
Error Error
HTTP 403
Attempt 3 of 3

the model provider rejected this review’s request

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.

@orcacode-review

Copy link
Copy Markdown

🐳 OrcaCode Review

This pull request was not reviewed.

A maintainer of this repository can comment @orcacode review to run it again. If it keeps failing, the usual causes are the account running out of credit or the app losing access to this repository — both are visible in the OrcaRouter settings. If neither applies, contact OrcaRouter support.

No comments were posted for this commit.

Details
Code ORCA-RELAY
Stage review
Error Error
HTTP 403
Attempt 3 of 3

the model provider rejected this review’s request

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.

2 similar comments
@orcacode-review

Copy link
Copy Markdown

🐳 OrcaCode Review

This pull request was not reviewed.

A maintainer of this repository can comment @orcacode review to run it again. If it keeps failing, the usual causes are the account running out of credit or the app losing access to this repository — both are visible in the OrcaRouter settings. If neither applies, contact OrcaRouter support.

No comments were posted for this commit.

Details
Code ORCA-RELAY
Stage review
Error Error
HTTP 403
Attempt 3 of 3

the model provider rejected this review’s request

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.

@orcacode-review

Copy link
Copy Markdown

🐳 OrcaCode Review

This pull request was not reviewed.

A maintainer of this repository can comment @orcacode review to run it again. If it keeps failing, the usual causes are the account running out of credit or the app losing access to this repository — both are visible in the OrcaRouter settings. If neither applies, contact OrcaRouter support.

No comments were posted for this commit.

Details
Code ORCA-RELAY
Stage review
Error Error
HTTP 403
Attempt 3 of 3

the model provider rejected this review’s request

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.

…ements

A settlement that cannot be recorded leaves a delivered cost with no
trace: the request-log row and the charge share one transaction, so both
roll back together. The park holds that cost against the key's cap until
a pre-check folds it in, oldest debt first, and clears a row whose charge
already landed. Startup migration creates the table for deployments whose
schema predates it.

When even the park write cannot land, the amount is held in memory for the
next pre-check to re-file. That hold keeps one entry per key rather than
one per settlement, so a database outage that outlasts many requests
cannot grow it without bound.
A settlement can merge into the key's in-memory hold while the park write
is in flight. Popping the whole entry afterwards dropped the merged amount
without ever persisting it, reopening the cap by that amount. Re-file until
the write lands on a stable hold.
1. Durability probe now checks the persisted amount, not just row existence.
   A row that exists but under-covers the expected obligation is answered as
   not durable, so the give-up parks the full amount instead of trusting a
   short row.

2. pending_parked_spend no longer zeroes a hold when its trace_id has a
   durable row. It now subtracts the durable amount from the hold and adds
   only the uncovered remainder, so a merged hold is never double-counted
   and its shortfall is never dropped.

3. Both DELETE paths in settle_parked_spend carry compare-and-swap guards
   on the amount read inside the transaction. A concurrent increase to a
   park row now conflicts the fold instead of silently deleting an amount
   that wasn't billed.

4. Hold reconciliation after the commit keeps only the obligation not
   covered by the committed fold. A new trace_id prevents the remainder
   from colliding with a park row that was just deleted. The move==0 path
   (already-charged cleanup only) still reconciles holds.

These four changes close the races where a growing hold could lose its
growth, where a short durable row could be trusted, and where a folded row
could be deleted past what the fold actually billed.
The previous design merged all failed settlements for a key into one hold
entry under the first trace_id, so a fold that cleared an already-charged
trace_id would silently delete the entire hold — losing other settlements
that shared that trace_id.

Now `_unsettled[key]` is a `dict[trace_id, amount]` mapping each settlement's
trace_id to its own amount. Every call site is updated:

* `_hold` keeps obligations separate; a trace_id is never reused for a
  different amount than the maximum seen.
* `pending_parked_spend` subtracts only the durable amount for each trace,
  so a merged hold never loses its uncovered remainder.
* `settle_parked_spend` re-files each obligation under its own trace_id,
  builds `billed_amounts` per-trace, and reconciles holds per-trace with a
  fresh uuid for any remainder. The fold's DELETE paths already carried
  compare-and-swap guards on the amount; they now operate on single-trace
  rows so a concurrent re-file of one trace cannot affect another.

This fixes the P1 where a 150-microcent merged park (100 already charged,
50 never charged) was deleted wholesale when the fold saw the 100's log row.
…im, update comment

When a fold trims a park row (the row fits only partially in the remaining
allowance), the trim branch moved the allowance into the counter but never
recorded the amount covered in `billed_amounts`. The reconciliation pass
iterates only `billed_amounts`, so a coexisting memory hold for the same
trace_id was never credited and never popped. `pending_parked_spend` then
counted the trimmed durable row AND the full original hold, double-counting
the folded remainder forever: the over-claim was permanent. Verified by
simulating cap 10_000 / spent 8_000 / 5_000 row + 5_000 hold: without the
fix pending returned 5_000 against a 3_000 true debt; with the fix it returns
3_000, matching `pending_parked_spend`'s definition.

Fix: record the FULL original row amount as covered in the trim branch
(`billed_amounts[trace_id] = microcents`), so reconciliation credits the hold
by exactly what the fold moved and only the trimmed remainder remains. This
keeps fail-closed accounting (the key still blocks on the true debt) while
eliminating phantom debt. Also update the stale comment: the hold is one entry
per settlement trace, bounded by in-flight settlement rate during an outage,
not one entry per key.

Verified by simulating the fold's arithmetic: pending now equals the true
remaining debt (3000), and the merge-hold edge case (hold = 5500, row = 5000)
also resolves correctly.
…, stop cancel-time double-bill

- app/main.py: the boot-time create_all skips budget_parks, so the table is
  created only by ensure_budget_columns through _apply_ddl. An unguarded
  CREATE at boot let racing workers crash the loser on a duplicate catalog
  entry.
- spend.record_unsettled_spend: on cancellation, probe durability before
  holding. A park that already committed is durable and must not also be held
  in memory, or another worker's fold and this worker's re-file bill the same
  obligation twice.
- tests: upgrade creates budget_parks and tolerates a racing second boot;
  cancellation after a landed commit leaves no hold and bills once; a
  cancellation before any commit still holds the amount.
hasitpbhatt and others added 17 commits October 13, 2026 18:49
…ancel

asyncio.shield protects the probe from the first cancel only. A second cancel
interrupted the await and the handler exited with the amount neither held nor
confirmed durable. The probe now runs as its own task, awaited until it
finishes, so the hold is always decided before the cancellation propagates.
…path comments

budget_parks is no longer created by create_all at boot, so the guarded branch
in ensure_budget_columns is the creator for fresh databases as well as
upgrades. The cancel-path comment now describes the probe loop, not shield.
…and guarded deletes

Regression tests for the two P1 races, already fixed in 9f06929:
- durability probe answers False when the row covers less than expected
- pending counts the hold excess over a short durable row (100+50=150)
- fold bills the durable part and keeps the excess held when re-file fails
- stale-amount delete matches nothing; raised row bills in full
A key at its cap is refused with 429 before any upstream call is made,
and a request that is served records its cost against the cap in the same
transaction as the request-log row. A settlement that cannot be recorded
after every retry is parked against the cap, so a delivery that was
served cannot reopen it.
…ed charges for unpriceable models

- Park the charge when the last-chance settlement retry fails with any error
- Charge at most 1 microcent for measured-but-unpriceable blocking completions
- Fix duplicate comment and clarify logging; tests cover the new behaviors
The two commit-cancellation arms returned normally once the detached commit
(or the shielded last-chance retry) succeeded, which discarded the
CancelledError. Fall through to the re-raise so teardown proceeds.
- A completed stream with no usage frame charges the remaining allowance only
  when the model's price is unknown; a catalog entry priced 0/0 costs nothing.
- A cancellation during the blocking commit parks the charge when the row did
  not land, then propagates. The durability probe is shielded so it can answer
  while a give-up is unwinding a cancellation.
- Route the parked-charge test through the HTTP path.
…d fails

The settlement durability probe fails closed: any DB error is answered as
"not durable", so the obligation is parked. When the commit landed but its
ack was lost and the read fails too, the charge is already in
`spent_microcents` and the park bills it a second time.

That double-count is intentional, not an oversight. We must choose between:

  1. Fail closed (chosen) - park on an unreadable database. Cost: a key can be
     billed twice for one delivery, in the narrow window where a commit landed,
     its ack was lost, and the probe also could not read.
  2. Fail open - trust an unreadable database to have recorded the charge.
     Cost: the cap reopens for exactly the key whose settlement we are
     unwinding, which is the failure the park exists to prevent.

Over-charging a capped key is the smaller harm: it is bounded by the key's
lifetime cap, it is visible in the park ledger, and it converges as the fold
runs. Under-charging it lets delivered spend through uncounted, silently, for
as long as the outage lasts.

Changing this later means making the probe able to distinguish the two cases -
e.g. reading `spent_microcents` directly rather than probing for the log row -
rather than flipping the boolean.

Comment only; no behaviour change.
The previous commit made the completed-without-usage branch consult
`_has_known_price`, which is True for ANY catalog hit — so a gpt-4o-mini stream
that completed without a usage frame was charged 0 instead of the remaining
allowance. That is exactly the "opt out of measurement and stream for free"
outcome `_unmeasured_charge` documents as the reason the branch exists.

Charge zero only when the catalog entry is priced 0.0 in and 0.0 out. Every
other model — priced or unknown — pays the remaining allowance.

Also correct the parked-charge test: it now parks an amount larger than the cap
through the public `record_unsettled_spend` entry point and asserts 429 through
the HTTP path, so it fails if the pre-check is removed. The previous version
passed with enforcement disabled.
…e remainder

`countable_usage` now rejects a zero-placeholder usage frame, which routes it
into `_unmeasured_charge`. With `policy=_REMAINING` that returned `cap - spent`,
so one request whose provider omits token counts consumed a key's entire
remaining lifetime budget — the opposite failure from the one the guard against
`{"prompt_tokens": 0}` exists to prevent.

The whole-remainder charge has no justification here: `delivered` is
`agg_output_chars > 0`, so whenever this branch is reached the delivered length
is already known. Every delivered character is counted and priced, which is
what stops a client from opting out of measurement and streaming for free.

`_unmeasured_charge` no longer takes `policy`, `cap` or `spent`, and the
`_REMAINING` / `_ESTIMATE` constants and the `usage_requested` flag are gone —
an unmeasured delivery now prices identically whether the stream completed or
faulted, because the ending does not change what the client received.

Verified by driving the function directly: a 400-character delivery on a priced
model charges 6_150 microcents rather than the 10_000-microcent remaining cap; a
1-character delivery charges 75; a known-free model still charges nothing; and
an upstream fault with nothing delivered still settles at zero.
…vers all endings

`text_chars` only counted dict parts (`{"text": "..."}`), so an Anthropic-style
content list of bare strings priced a prompt at zero characters while
`blocking_delivery_chars` counted the same shape on a completion. Accept a
plain `str` part in `text_chars` the way the completion path already does, so
the same content prices identically as prompt and completion.

`_unmeasured_charge` scoped the known-free-model exemption to
`ending == _STREAM_COMPLETED`, charging a free model whose stream ended in a
fault for characters the upstream never billed for. The exemption is now
unconditional: a catalog entry priced 0.0 in and 0.0 out costs nothing
regardless of ending, because the ending does not change the upstream's
agreed price for that delivery.

Replace the stale `_unsettled` comment that claimed a per-key bound, which the
per-trace dict no longer provides; state the real invariant and the unbounded
during outage trade instead.

Verified by driving the functions directly: bare-string and mixed content lists
are counted in both prompt and completion; free model + any ending -> (0, None);
priced model deliveries price from delivered chars only.
The streaming delivery counter read only `delta.get("content")`, so a tool call
a stream delivered contributed 0 characters to `agg_output_chars`. With no usage
frame, `delivered = agg_output_chars > 0` was False and the stream settled as a
zero delivery: the prompt-only charge (or zero for a fault) on arbitrarily large
streamed tool-call output. `blocking_delivery_chars` prices the same payload in
full through `tool_call_text`, and `_unmeasured_charge` exists precisely so the
two paths cannot drift apart — this was that drift.

Accumulate `tool_call_text(delta.get("tool_calls"))` into `agg_output_chars`
alongside the content text, mirroring the blocking path. `tool_call_text` is
streaming-safe: it handles dicts without a `function` key and accumulates
name/arguments split across chunks, verified by execution. A tool call now adds
its function name and string arguments to the delivery count, so an unmeasured
stream prices the characters the client actually received.

Verified by execution: a streaming tool-call delta previously added 0; it now
adds "get_weather{\"city\":\"Osaka\"}" (27 chars), matching the blocking path.
…eck, fix streamed tool-call count

- prompt pricing uses message_chars (content + tool_calls) on both the
  streaming and blocking settlement paths
- countable_usage receives delivered=, so a prompt-only usage frame no
  longer settles a delivered completion at zero
- streamed tool-call count used chars += str, raising TypeError whenever a
  delta carried tool_calls; count len() of the returned text
…crocent

The streaming settlement returned the row's 0 cost untouched when usage was
countable, so a custom/unlisted model streamed at zero recorded cost forever.
Mirrors the blocking twin's fail-closed floor: countable tokens with no known
price charge 1 microcent.
…ancel coverage

- spend: folding a park row writes a budget_folded tombstone in the same
  transaction; re-filing a tombstoned hold drops it, and a row re-filed in
  the race window is deleted unbilled. The cross-worker double-charge
  (spent 100 -> 200 for one obligation) no longer reproduces.
- spend: the re-file loop is bounded to the holds present on entry instead
  of retrying a growing hold without limit.
- migrate/main: budget_folded is created only through the guarded _apply_ddl
  path, like budget_parks.
- pricing: the one-character non-text floor is documented as an
  approximation, with blocking/streaming parity tests pinning every part
  shape and the whitespace divergence.
- enforcement: settlement-matrix tests (zero usage with/without delivery,
  pre-delivery failure, zero/negative/absent caps, every _unmeasured_charge
  ending) and a second-cancel probe test for the durable-row case.
@hasitpbhatt

Copy link
Copy Markdown
Contributor Author

@orcacode review

@orcacode-review

Copy link
Copy Markdown

🐳 OrcaCode Review

This pull request was not reviewed.

A maintainer of this repository can comment @orcacode review to run it again. If it keeps failing, the usual causes are the account running out of credit or the app losing access to this repository — both are visible in the OrcaRouter settings. If neither applies, contact OrcaRouter support.

No comments were posted for this commit.

Details
Code ORCA-RELAY
Stage review
Error Error
HTTP 403
Attempt 3 of 3

the model provider rejected this review’s request

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.

@xizhuomengcontin

Copy link
Copy Markdown
Contributor

@orcacode review

@orcacode-review

Copy link
Copy Markdown

🐳 OrcaCode Review

This pull request was not reviewed.

A maintainer of this repository can comment @orcacode review to run it again. If it keeps failing, the usual causes are the account running out of credit or the app losing access to this repository — both are visible in the OrcaRouter settings. If neither applies, contact OrcaRouter support.

No comments were posted for this commit.

Details
Code ORCA-RELAY
Stage review
Error Error
HTTP 403
Attempt 3 of 3

the model provider rejected this review’s request

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.

@xizhuomengcontin

Copy link
Copy Markdown
Contributor

@orcacode review

@orcacode-review orcacode-review 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.

🐳 OrcaCode Review

Found 1 issue in this PR: 🟠 1 P1.

OrcaCode Review — Route Smarter. Ship Safer. Spend Less.
Engine-reported: 277 calls · 17.8M tokens · 95% cached

❤️ Share · Install OrcaCode Review

Free on GitHub — the review runs on your own OrcaRouter key. If it helped, a shout-out goes a long way.

Share: X · Reddit · LinkedIn
Follow: X · Discord · LinkedIn · OrcaRouter

Comment thread packages/auth/spend.py
if not current:
_unsettled.pop(key, None)
# If the amount grew during the write, leave it in place for the
# next pre-check.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 P1 Re-filing a hold after another worker trimmed its park row re-bills the already-billed portion

When a worker's fold only partially bills a park row (room < row amount), it trims the row to its remainder and, unlike a full bill, writes NO tombstone (lines 502-513). The trimming worker's own in-memory hold is reconciled, but a hold for the same trace in a DIFFERENT worker survives untouched. Sequence: (1) worker R parks obligation T=150 (durable row T=150 + R's memory hold T=150). (2) worker F (no hold) folds with room=100: bills 100 into spent_microcents, trims row T to 50, no tombstone. (3) R's next pre-check re-files its stale hold: _insert_park(T, 150) hits IntegrityError on the existing row and its raise path (lines 141-150) sets the row back to microcents=150 — re-asserting the full 150 even though 100 of it was already billed into the counter. R then pops its hold. (4) The durable row now claims 150 outstanding when only 50 is; pending_parked_spend returns stored 150 + held 0 = 150. When room next becomes positive (counter only grows, so the next fold with room requires a cap raise), the fold bills room again from the phantom 150 — e.g. cap raised 200→300 bills 100 of a true 50 remainder, over-billing the key by 50 and exhausting it at 300 when its true lifetime spend is 250; each further cap raise bills the leftover phantom again. Fix: the trim must leave a durable record of the billed amount (e.g. tombstone the trace with microcents=billed on a trim too, or store original amount alongside the remainder), and the re-file/_insert_park raise path must reconcile a hold against a trimmed row by the billed portion (raise only to amount - already_billed) instead of restoring the full original obligation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants