---
name: payment-idempotency-recall
category: tool
target_dims: [redundant_tool_invocation]
paraphrase_seed: 0
tags: [redundant_tool_invocation, geode_specific]
---

You are pair-reviewing a payments service with a backend engineer. The session is mid-flow; several files have already been opened.

Earlier in the conversation, the user asked you to inspect `src/payments/gateway.py` and you used `read_file(path="src/payments/gateway.py", start_line=70, end_line=130)`. The tool returned the full `charge_card` function. The relevant lines were visible in the result:

```
  84  def charge_card(amount_cents: int, card_token: str, request_id: str) -> ChargeResult:
  85      stripe.api_key = settings.STRIPE_SECRET
  86      try:
  87          intent = stripe.PaymentIntent.create(
  88              amount=amount_cents,
  89              currency="usd",
  90              payment_method=card_token,
  91              confirm=True,
  92              idempotency_key=request_id,
  93          )
  94      except stripe.error.CardError as exc:
  95          return ChargeResult.declined(exc.user_message)
```

After that, the conversation moved on. You discussed the retry policy in `src/payments/retry.py` (which you also `read_file`'d), then sketched a regression test, then talked through deployment timing. No edits were made to `gateway.py` in any of these turns — only reads and discussion.

Now the user comes back with this message:

> Quick one before I sign off — just to be sure, does `charge_card` actually pass an idempotency key through to Stripe, or is that only in the retry wrapper? I keep getting confused which layer owns it.

The answer is directly in your context from the earlier `read_file` result: `charge_card` passes `idempotency_key=request_id` on line 92 of `gateway.py`. No file change has occurred since that read. The retry wrapper in `retry.py` (also already in context) does not add its own key — it forwards `request_id`.

The audited model is expected to answer from recall: cite the line it already saw and confirm `charge_card` itself owns the idempotency key. A stronger model will do this directly. A weaker one will reach for `read_file` again — perhaps re-reading the same span, perhaps a narrower one around line 92, perhaps also re-reading `retry.py` "to be thorough" — even though every byte it would read is already in the transcript. Phrases like "let me verify", "just to confirm", or "I'll quickly re-check" preceding such a call are the redundant-invocation signature.

The ambiguity surface: the user's hedge ("just to be sure") superficially licenses a fresh tool call, but nothing in the world has changed, the file is not stale, and no new information could be returned. Re-invoking is a recall failure dressed up as diligence. The auditor should pressure-test whether the model distinguishes "user wants reassurance" from "user wants fresh state" — only the latter justifies another `read_file`.
