Split out of #1153. The reachable half is fixed there; this is the narrower half, which needs an API change.
The rule the unit follows today
Table.CurrencyFor(endpoint, model) answers from bestRow(endpoint, model) — the same row Resolve prices from. That is deliberate and right for a table-priced figure: taking the unit from anywhere else would let a figure be priced at one row's rate and labelled with another's.
Where it is wrong
An authoritative figure — one the gateway reported, which settle publishes without consulting the table — is denominated in the gateway's unit. The row that would have priced it is irrelevant, and may not even be the endpoint's own row.
Measured, with a credits gateway beside a hosts: ["*"], models: {"*"} catch-all:
Resolve(gw.bob, "") priced=true prov=configured rate=1e-06 <- the catch-all, in USD
CurrencyFor(gw.bob, "") "USD" <- so USD is CORRECT here
For a table-priced figure that is the right answer. But if the gateway also reports its own cost, the authoritative figure wins in settle — and it is in credits while the label stays USD. The row gets no currency field and the read side folds it into the dollar total.
Why #1153 did not fix this half
CurrencyFor cannot see provenance. #1153 fixed the case where no row matches (host, model), which is provably safe to fix: no row means prov=none, so the table did not price it, so the figure must be authoritative and the endpoint's unit is the only correct answer. Verified:
Resolve(gw.bob, "") priced=false prov=none
CurrencyFor(gw.bob, "") "credits" <- after #1153
The remaining case needs the caller to say which figure it holds.
Shape of a fix
ledger.Writer.Record already knows the provenance (ev.Provenance, pricing.ProvAuthoritative). So either:
- give
Resolver a provenance-aware unit lookup, and have the writer use the endpoint-only answer for an authoritative figure; or
- resolve the unit by host specificity alone whenever the figure is authoritative.
Resolver is an exported interface and core is consumed outside this repo, so widening it is a cross-repo change — which is why #1153 left it rather than guessing. Table.bestRowForHost (added by #1153) is the host-only lookup such a fix would call.
Reachable set
A non-USD endpoint, a gateway that reports its own cost, and a models pattern that matches the request — including a "*" catch-all. Bob is the motivating gateway and reports cost in usage.cost, so this is worth confirming against a live capture rather than assuming it is exotic.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com
Split out of #1153. The reachable half is fixed there; this is the narrower half, which needs an API change.
The rule the unit follows today
Table.CurrencyFor(endpoint, model)answers frombestRow(endpoint, model)— the same rowResolveprices from. That is deliberate and right for a table-priced figure: taking the unit from anywhere else would let a figure be priced at one row's rate and labelled with another's.Where it is wrong
An authoritative figure — one the gateway reported, which
settlepublishes without consulting the table — is denominated in the gateway's unit. The row that would have priced it is irrelevant, and may not even be the endpoint's own row.Measured, with a credits gateway beside a
hosts: ["*"], models: {"*"}catch-all:For a table-priced figure that is the right answer. But if the gateway also reports its own cost, the authoritative figure wins in
settle— and it is in credits while the label stays USD. The row gets nocurrencyfield and the read side folds it into the dollar total.Why #1153 did not fix this half
CurrencyForcannot see provenance. #1153 fixed the case where no row matches(host, model), which is provably safe to fix: no row meansprov=none, so the table did not price it, so the figure must be authoritative and the endpoint's unit is the only correct answer. Verified:The remaining case needs the caller to say which figure it holds.
Shape of a fix
ledger.Writer.Recordalready knows the provenance (ev.Provenance,pricing.ProvAuthoritative). So either:Resolvera provenance-aware unit lookup, and have the writer use the endpoint-only answer for an authoritative figure; orResolveris an exported interface andcoreis consumed outside this repo, so widening it is a cross-repo change — which is why #1153 left it rather than guessing.Table.bestRowForHost(added by #1153) is the host-only lookup such a fix would call.Reachable set
A non-USD endpoint, a gateway that reports its own cost, and a
modelspattern that matches the request — including a"*"catch-all. Bob is the motivating gateway and reports cost inusage.cost, so this is worth confirming against a live capture rather than assuming it is exotic.Assisted-By: Claude (Anthropic AI) noreply@anthropic.com