Skip to content

fix(supervise): price a bridge turn that carried no provider receipt - #840

Merged
drewstone merged 4 commits into
mainfrom
fix/dollar-channel-estimate
Aug 14, 2026
Merged

fix(supervise): price a bridge turn that carried no provider receipt#840
drewstone merged 4 commits into
mainfrom
fix/dollar-channel-estimate

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Problem

A cli-bridge turn whose provider reported no billed dollars reached the dollar channel as a known $0 (supervise/runtime.ts, the yield { kind: 'cost', usd: 0, usdKnown: false } at turn settle).

Measured: across 292 fleet runs under discovery-lab/pursuits, spentTotal.usd > 0 on zero runs. The dollar channel reported nothing on runs that certainly spent money.

Pairs with drewstone/cli-bridge#159, which forwards claude's total_cost_usd as a real provider receipt. This PR covers everything that has no receipt to forward.

Change

Price such a turn from the model catalog against that turn's own token counts, using the response model the bridge already sends on every chunk. Nothing new on the wire.

New src/runtime/supervise/cost-estimate.tspriceUnreceiptedWork({ inputTokens, outputTokens, model }).

A receipt and an estimate must not read the same, so the priced part is carried explicitly:

Field Meaning
Spend.usd all dollars attributed
Spend.usdEstimated the part priced from the catalog
usd - usdEstimated what a provider is known to have billed
usdKnown whether the total is a measurement at all

usdEstimated is added to the cost UsageEvent and to Spend, and folded through every merge site (spendFromUsageEvents, foldUsage, spawn-journal clone/add, trajectory clone/add, supervisor accumulate/add). usdEstimatedOf in runtime/util.ts returns nothing when no input carried one, so a pure-receipt fold never gains a usdEstimated: 0 that would read as "checked, none estimated".

assertValidSpend refuses an estimated part that is negative, non-finite, exceeds usd, or claims usdKnown: true.

Rules that keep this honest

  • Only a turn that billed NOTHING is priced. A turn holding a partial receipt already put real dollars on the channel; a whole-turn catalog price on top would charge the same tokens twice.
  • An unpriced model contributes no dollars and leaves the turn usdKnown: false — unknown, never free.
  • Cache reads are priced at the full input rate. The catalog entry is { input, output } per model with no cache-read rate (verified on the published @tangle-network/agent-eval@0.145.2 .d.ts). That overstates a cache-heavy turn, which is the correct direction — an invented discount would understate spend. Asserted in the tests rather than left as a comment.
  • A dollar cap is unaffected. observe and reconcile still refuse unknown dollars under a maxUsd root, and an estimate rides usdKnown: false. Test included.

Proof

tests/kernel/dollar-estimate.test.ts — 14 new tests.

Behavioral proof on the bridge path: stash src/runtime/supervise/runtime.ts, run bridge-executor.test.ts:

 × prices a turn the bridge reported no price for, and keeps the dollars unknown
 × keeps dollar cost unknown when a later completed turn omits price
 × interrupts an active response body, accounts its partial usage, and resumes with the steer
AssertionError: expected [ …(3) ] to deep equally contain { kind: 'cost', usd: 0.0000062, …(2) }
 Tests  3 failed | 28 passed (31)

With the change:

 Test Files  1 passed (1)
      Tests  31 passed (31)

Full suite:

 Test Files  214 passed | 2 skipped (216)
      Tests  2669 passed | 6 skipped (2675)
   Duration  32.65s

pnpm typecheck and pnpm lint clean.

What I did NOT change, and why

routerToolsInlineExecutor (which chatTransportExecutor builds on) has the same "no receipt → $0" shape, and I initially fixed it too. tests/kernel/chat-transport-executor.test.ts then failed on a test named "marks tokensKnown/usdKnown false when the transport omits usage or cost — never estimates", whose inline comment reads "the priced model id must NOT tempt a local estimate."

That is a deliberate, documented contract in the opposite direction, written for a bare caller-supplied transport. I reverted that half rather than flip another author's stated invariant unasked. Flagging it for a decision instead.

Docs

docs/architecture.md gains the dollar-channel section next to the token-channel rules: receipt vs price, what usdEstimated means, the cache-rate gap, and the unchanged cap behavior.

A cli-bridge turn whose provider reported no billed dollars reached the
dollar channel as a known $0. Across 292 fleet runs `spentTotal.usd > 0` was
true on none of them, so the dollar channel reported nothing on runs that
certainly spent money.

Price such a turn from the model catalog against that turn's OWN token
counts, using the response `model` the bridge already sends on every chunk.
The event always carries `usdKnown: false`, and the priced part rides a new
`usdEstimated` on both the cost event and `Spend`, so `usd - usdEstimated`
is what a provider is known to have billed. `assertValidSpend` refuses an
estimated part that exceeds `usd` or that claims `usdKnown: true`.

Only a turn that billed NOTHING is priced. A turn holding a partial receipt
already put real dollars on the channel, and a whole-turn catalog price on
top would charge the same tokens twice. An unpriced model contributes no
dollars and leaves the turn unknown rather than free.

The catalog holds one input rate and one output rate per model and no
cache-read rate, so a prompt prefix served from cache is priced at the full
input rate. That overstates a cache-heavy turn, which is the correct
direction: an invented discount would understate spend.

A dollar cap is unaffected. `observe` and `reconcile` still refuse unknown
dollars under a `maxUsd` root, and an estimate rides `usdKnown: false`.

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — bc2f0adc

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

This approval is provisional. It rests on the audit running. If the audit cannot run — for example the CLI bridge rejects it — this approval is dismissed rather than left standing, so an unrun check never reads as a passing one.

tangletools · auto-approval · reason: drewstone_author · 2026-08-14T05:36:29Z

tangletools
tangletools previously approved these changes Aug 14, 2026

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — bc2f0adc

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

This approval is provisional. It rests on the audit running. If the audit cannot run — for example the CLI bridge rejects it — this approval is dismissed rather than left standing, so an unrun check never reads as a passing one.

tangletools · auto-approval · reason: drewstone_author · 2026-08-14T05:37:41Z

…eiling

`reconcile` compared a child's dollars against its reservation before it
checked whether those dollars were measured at all. A catalog-priced turn can
exceed the ceiling, so an unreceipted child was reported as spending more
than it reserved — a claim that the child spent dollars a provider billed.

Decide `unknownUnderCap` first. Dollars that are not measured have no
business being compared to a dollar reservation, and the accurate diagnosis
is the unknown-cost refusal. Both paths still throw and still close dollar
admission, so no run outcome changes; the reason a caller reads does.
tangletools
tangletools previously approved these changes Aug 14, 2026

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — 4cc0f3f8

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

This approval is provisional. It rests on the audit running. If the audit cannot run — for example the CLI bridge rejects it — this approval is dismissed rather than left standing, so an unrun check never reads as a passing one.

tangletools · auto-approval · reason: drewstone_author · 2026-08-14T05:55:20Z

Neither repo covered the seam between them. This drives the bridge executor
with the exact usage object cli-bridge emits for a claude turn carrying
total_cost_usd, captured off deltaToOpenAIChunk: tokens and the receipt in
ONE frame.

The turn must settle as measured dollars with no estimated part. A receipt
is a measurement, and a turn holding one is never catalog-priced on top.

@tangletools tangletools 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.

✅ Auto-approved drewstone PR — 8a1c4322

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

This approval is provisional. It rests on the audit running. If the audit cannot run — for example the CLI bridge rejects it — this approval is dismissed rather than left standing, so an unrun check never reads as a passing one.

tangletools · auto-approval · reason: drewstone_author · 2026-08-14T05:57:44Z

@drewstone
drewstone merged commit 60b97b6 into main Aug 14, 2026
4 checks passed
@drewstone
drewstone deleted the fix/dollar-channel-estimate branch August 14, 2026 06:02
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