[test] Receipts: your own settled calls, in the shape a finance system posts - #1306
Merged
Conversation
…m posts my-usage answers "how much have I spent" for a person reading a report. An accounting system needs a different shape: one row per settled payment with what was bought, what settled, and the evidence. POST /api/receipts ($0.005) returns exactly that, plus format:"csv" for a subledger import. Every row carries three things that make it auditable by someone who does not trust us: the settlement transaction (checkable on the named chain), the sha256 of the bytes delivered, and the EAS attestation id where one was written. That is the difference between a receipt and a number we assert. IDENTITY-BOUND BY THE SIGNATURE, never a parameter. The wallet comes from the verified EIP-3009 authorization, so the route can only ever return the caller's own payables - a global feed of who paid whom is the customer list we refuse to publish anywhere else, and a finance system does not want one. Added to isIdentityBoundRoute, so it advertises EVM exact only and a Solana or Stellar buyer is never charged for a call the server cannot answer. test-capped-counts caught a defect in this commit's own code: `count` was rows.length of a LIMITed query. That is the exact shape that once published a LIMIT-20 length as "tools used", and for payables it would silently under-report with no way for the reader to tell. Now `returned` (this page) beside an UNCAPPED `total` (the window), with truncated derived from the two. CSV quotes every field, doubles embedded quotes, and prefixes a leading =,+,-,@ because spreadsheet software executes those and these rows carry third-party slugs. 32 assertions, 4 mutations killed (return everyone's rows, include internal rows, drop the settlement tx, stop being identity-bound). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQbt8oAJjLVbTKY2GqviH6
The receipts entry went in as USAGE_TOOLS[0], and scripts/test-usage.js reads
USAGE_TOOLS[0] positionally - so that suite was asserting my-usage's contract
against the receipts handler and failed in CI on the very PR that added it.
Two fixes, because either alone leaves the trap set:
- receipts is APPENDED, with a note on the array saying consumers index it
positionally and a new tool goes on the end;
- the test selects by slug, so a future append cannot re-point it either.
Caught by CI on the push run, not merged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQbt8oAJjLVbTKY2GqviH6
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.
my-usageanswers "how much have I spent" for a person reading a report. An accounting system needs a different shape: one row per settled payment with what was bought, what settled, and the evidence.POST /api/receipts($0.005) returns that, plusformat:"csv"for a subledger import.What makes a row worth having
Every row carries three things that make it auditable by someone who does not trust us: the settlement transaction (checkable on the named chain), the sha256 of the bytes delivered, and the EAS attestation id where one was written. That is the difference between a receipt and a number we assert.
It also carries
amountUsdbesidequotedUsd— on a metered call the gap between the authorized ceiling and what actually settled is the thing a buyer reconciles.Identity-bound by the signature, never a parameter
The wallet comes from the verified EIP-3009 authorization, so the route can only return the caller's own payables. A global feed of who paid whom is the customer list we refuse to publish anywhere else — and a finance system doesn't want one, it wants its own.
Added to
isIdentityBoundRoute, so it advertises EVM exact only and a Solana or Stellar buyer is never charged for a call the server cannot answer.A defect caught by an existing guard
test-capped-countsfailed on this commit's own code:countwasrows.lengthof a LIMITed query — the exact shape that once published a LIMIT-20 length as "tools used". For payables it would silently under-report with no way for the reader to tell. Nowreturned(this page) beside an uncappedtotal(the window), withtruncatedderived from the two.CSV quotes every field, doubles embedded quotes, and prefixes a leading
=,+,-,@— spreadsheet software executes those, and these rows carry third-party slugs.32 assertions, 4 mutations killed.
test-sales-ledger,test-capped-counts,test-free-tier-egress,sync-count, self-consistency and docs-truth all green.🤖 Generated with Claude Code
https://claude.ai/code/session_01LQbt8oAJjLVbTKY2GqviH6