Repository navigation
spec(1.1): permit the decimal-string form for monetary amounts - #22
Merged
Merged
Conversation
Rebuilt onto current master; the branch had diverged at the root, so this is the change reapplied as one commit rather than a merge of unrelated histories. The only conflict was in CHANGE_LOG.md, where master had gained a newer entry at the same position — both entries are kept, ordered newest-first as the file requires. Also drops the `tools/__pycache__/*.pyc` this branch had accidentally committed, and adds a .gitignore so it cannot come back. The repo had no ignore rule, which is how it got in. schemas / refs / vectors / coherence all pass.
maoryeh
force-pushed
the
spec/integer-wire-encoding
branch
from
September 21, 2026 11:58
3388985 to
4b8a483
Compare
yahalomran
enabled auto-merge (squash)
September 21, 2026 12:47
yahalomran
disabled auto-merge
September 21, 2026 12:47
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
"type": "integer"on a monetary field is a constraint no Protobuf-generated implementation can satisfy.The canonical Protocol Buffers JSON mapping serializes 64-bit integer fields as decimal strings, and does so unconditionally. From protobuf-java's own
JsonFormat— note there is no flag, unlike the 32-bit case immediately above it:The reason is that JSON numbers are IEEE 754 doubles and cannot represent every 64-bit integer exactly. So an implementation that models money as
int64— the natural choice, since minor units in a zero-decimal currency overflowint32at roughly 21 million major units — emits"amount": "35000"and fails validation against its own protocol.Found while bringing an implementation into conformance: every monetary field it produced was rejected, and no amount of implementation work could fix it.
What changes
Nineteen monetary fields in the canonical schemas, plus four defined inline in the REST and MCP bindings, now accept either form:
schemas/booking.jsonBookingPayment.{amount, amount_due, deposit_amount, tax_amount},PaymentContext.{amount_due, tax_amount, line_items[].amount},Cancellation.{fee, refund_amount}schemas/catalog.jsonPricing.{amount, price_range.min, price_range.max, deposit.value},ServicePolicies.{cancellation.late_cancellation_fee, rescheduling.fee, no_show.fee}schemas/availability.jsonTimeSlot.pricing.amountschemas/registry.jsonPriceRangeFilter.{min, max}openapi/usp-rest.json,openrpc/usp-mcp.jsonfilters.price.{min, max},payment_result.amount_paidAnd §1.1 gains an Integer Encoding subsection stating the rule normatively:
^-?[0-9]+$Two deliberate choices
The pattern is not decoration. A bare
stringwould let a producer send"3.5e4"or"1,500"for a price and still validate. Verified behaviour:pricing.deposit.valuetakes^-?[0-9]+(\.[0-9]+)?$instead, because it already admitsnumberto carry a percentage whendeposit.typeispercentage.Bounded-domain integers stay strict. Counts, capacities, party sizes, percentages, pagination limits, waitlist positions and HTTP status remain
integerand MUST be JSON numbers. They fit a 32-bit integer, are not subject to the 64-bit mapping, and widening them would cost type safety for no interoperability gain.Rating.valueis likewise untouched — it is a score, not money.Alternative considered
Narrowing the fields to
int32in implementations instead. Rejected: it caps money at 2,147,483,647 minor units (~$21.5M, or ~¥2.1B in a zero-decimal currency) to fix a presentation detail, and it would not help any implementation that has already chosenint64. Since the constraint is inherited from the IDL rather than chosen, the spec is the right place to fix it — this is also what Google's own REST APIs do.Verification
tools/usp_check.py schemas,refsandvectorsall pass, withcryptographyinstalled so the vector signatures are genuinely verified rather than silently skipped.