Skip to content

cost.budget.remaining metric coerces Decimal budget to float and loses currency precision #55

@nficano

Description

@nficano

src/arcp/_runtime/job.py:222 emits a cost.budget.remaining follow-up metric with "value": float(remaining), where remaining is a Decimal produced by Job.apply_cost_metric (see src/arcp/_runtime/job.py:78-80). The conversion float(Decimal) is the textbook example of why Decimal exists in the standard library: a budget of Decimal("0.1") + Decimal("0.2") becomes 0.30000000000000004 on the wire, and a client receiving the metric cannot recover the exact remaining amount. The same coercion happens implicitly for the incoming value at src/arcp/_runtime/job.py:209 via float(body.get("value", 0)), which is then re-wrapped as Decimal(str(value)) in apply_cost_metric — losing precision before the budget arithmetic even runs. For a v1.1 protocol that publishes currency amounts as currency:decimal strings (src/arcp/_messages/execution.py:53), exposing them as floats on the metric path is a step backward from the wire format itself.

Fix prompt: Keep Decimal end-to-end on the metric path. In JobContext.metric at src/arcp/_runtime/job.py:205, accept value as either Decimal | str | int | float and normalize via Decimal(str(value)) before calling apply_cost_metric. When emitting the follow-up metric at src/arcp/_runtime/job.py:218, serialize remaining as the canonical f"{remaining}" string with the existing currency rather than float(remaining), and update the consumer-side parsing helpers accordingly. If the wire format truly requires a JSON number for the metric body, document the precision contract explicitly in the spec section linked from EVENT_KINDS and round to the documented number of decimal places using Decimal.quantize. Add a property-based Hypothesis test that round-trips arbitrary Decimal budget amounts through emit/parse and asserts equality.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingseverity:lowLow severity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions