Shipping refund handling, we wrote this into the spec:
Chargebacks (
charge.dispute.created). They are the same motion, but a dispute object carries the charge id rather than our metadata, so attributing one needs a charge-to-account index this service does not keep. Implementing it half-way — guessing an account from a payment intent we never recorded — would be worse than the current honest gap.
Good practice. The gap is named, the reason is given, and the reason is technical rather than "we ran out of time".
It was also wrong by the time it was committed, because of a change in the same pull request.
The change that invalidated it
A refund event carries the charge, not the Checkout session, so the account id had to reach the charge somehow. The fix was one argument:
session = stripe.checkout.Session.create(
...,
metadata={"account_id": account["id"]},
payment_intent_data={"metadata": {"account_id": account["id"]}},
)
A charge inherits its PaymentIntent's metadata. So from that commit onward, every charge we create carries the account id.
And a dispute carries the charge. Which means:
dispute.charge → retrieve the charge → metadata.account_id
One API call, on a path that fires when a human disputes a payment rather than per request. Exactly the pattern the refund handler already used when an event did not carry what it needed.
The index we said we did not keep — we had just started keeping it, in Stripe, as a side effect of solving the adjacent problem.
Why it survived a day
Nothing was wrong. The tests passed, the gate was green, the spec was internally consistent, and the sentence describing the limitation read exactly as true as it had when it was written.
The only thing that could have caught it is somebody re-reading a paragraph
about a feature they were not building, in a file they had no reason to open,
because a different paragraph in the same file had changed. There is no CI check
for that. PAC-902 will tell you when a test reference goes stale, because a
test path is a thing a machine can resolve. "This justification no longer holds"
is not.
It was found by a question — what do I do about chargebacks — which forced a re-read.
What we changed, besides the code
The paragraph is not deleted. The spec now says:
This spec previously called chargebacks out of scope, on the grounds that attributing one needed a charge-to-account index this service does not keep. That was true when it was written and stopped being true in the same change that added the PaymentIntent metadata for refunds. The note was simply not revisited.
It is recorded here rather than quietly deleted, because "we decided this was impossible and then made it possible without noticing" is a failure mode worth being able to recognise a second time.
Deleting it would have left a spec that looks like it always intended to handle disputes, and a team with no memory of how the gap closed itself.
The general shape
A documented limitation is a claim about the current state of the system, and claims decay. This one decayed because of a change made deliberately, by the same people, in the same commit — which is the least likely circumstance to notice and the most likely to occur.
The habit we are trying to build: when a change makes something newly possible, grep the specs for the reason you previously said it was not. The reason is usually a specific technical fact, and specific technical facts are searchable.
charge-to-account index would have found it in a second.