Skip to content

[test] Receipts: your own settled calls, in the shape a finance system posts - #1306

Merged
MikeyPetrillo merged 2 commits into
mainfrom
claude/sweet-brown-i99jl3
Sep 11, 2026
Merged

[test] Receipts: your own settled calls, in the shape a finance system posts#1306
MikeyPetrillo merged 2 commits into
mainfrom
claude/sweet-brown-i99jl3

Conversation

@MikeyPetrillo

Copy link
Copy Markdown
Owner

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 that, plus format:"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 amountUsd beside quotedUsd — 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-counts failed on this commit's own code: count was rows.length of 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. 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 =, +, -, @ — 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

MikeyPetrillo and others added 2 commits September 11, 2026 17:08
…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
@MikeyPetrillo
MikeyPetrillo merged commit 9aa24c4 into main Sep 11, 2026
69 checks passed
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.

1 participant