@pome-sh/cli 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/src/cli/checks.d.ts +14 -0
  3. package/dist/src/cli/checks.js +27 -3
  4. package/node_modules/@pome-sh/twin-gmail/CHANGELOG.md +39 -0
  5. package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.d.ts +7 -0
  6. package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js +138 -0
  7. package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js.map +1 -0
  8. package/node_modules/@pome-sh/twin-gmail/dist/src/check-kind.d.ts +3 -0
  9. package/node_modules/@pome-sh/twin-gmail/dist/src/check-kind.js +9 -0
  10. package/node_modules/@pome-sh/twin-gmail/dist/src/check-kind.js.map +1 -0
  11. package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.d.ts +4 -0
  12. package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js +56 -0
  13. package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js.map +1 -0
  14. package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.d.ts +14 -0
  15. package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js +207 -0
  16. package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js.map +1 -0
  17. package/node_modules/@pome-sh/twin-gmail/dist/src/check-params.d.ts +10 -0
  18. package/node_modules/@pome-sh/twin-gmail/dist/src/check-params.js +96 -0
  19. package/node_modules/@pome-sh/twin-gmail/dist/src/check-params.js.map +1 -0
  20. package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.d.ts +113 -0
  21. package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js +145 -0
  22. package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js.map +1 -0
  23. package/node_modules/@pome-sh/twin-gmail/dist/src/check-tape.d.ts +2 -0
  24. package/node_modules/@pome-sh/twin-gmail/dist/src/check-tape.js +103 -0
  25. package/node_modules/@pome-sh/twin-gmail/dist/src/check-tape.js.map +1 -0
  26. package/node_modules/@pome-sh/twin-gmail/dist/src/check-worlds.d.ts +17 -0
  27. package/node_modules/@pome-sh/twin-gmail/dist/src/check-worlds.js +62 -0
  28. package/node_modules/@pome-sh/twin-gmail/dist/src/check-worlds.js.map +1 -0
  29. package/node_modules/@pome-sh/twin-gmail/dist/src/checks.d.ts +19 -0
  30. package/node_modules/@pome-sh/twin-gmail/dist/src/checks.js +42 -0
  31. package/node_modules/@pome-sh/twin-gmail/dist/src/checks.js.map +1 -0
  32. package/node_modules/@pome-sh/twin-gmail/dist/src/index.d.ts +2 -0
  33. package/node_modules/@pome-sh/twin-gmail/dist/src/index.js +4 -0
  34. package/node_modules/@pome-sh/twin-gmail/dist/src/index.js.map +1 -1
  35. package/node_modules/@pome-sh/twin-gmail/package.json +1 -1
  36. package/node_modules/@pome-sh/twin-stripe/dist/src/check-kind.d.ts +3 -0
  37. package/node_modules/@pome-sh/twin-stripe/dist/src/check-kind.js +9 -0
  38. package/node_modules/@pome-sh/twin-stripe/dist/src/check-kind.js.map +1 -0
  39. package/node_modules/@pome-sh/twin-stripe/dist/src/check-params.d.ts +8 -0
  40. package/node_modules/@pome-sh/twin-stripe/dist/src/check-params.js +123 -0
  41. package/node_modules/@pome-sh/twin-stripe/dist/src/check-params.js.map +1 -0
  42. package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.d.ts +18 -0
  43. package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js +264 -0
  44. package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js.map +1 -0
  45. package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.d.ts +8 -0
  46. package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js +122 -0
  47. package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js.map +1 -0
  48. package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.d.ts +83 -0
  49. package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js +95 -0
  50. package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js.map +1 -0
  51. package/node_modules/@pome-sh/twin-stripe/dist/src/check-tape.d.ts +9 -0
  52. package/node_modules/@pome-sh/twin-stripe/dist/src/check-tape.js +338 -0
  53. package/node_modules/@pome-sh/twin-stripe/dist/src/check-tape.js.map +1 -0
  54. package/node_modules/@pome-sh/twin-stripe/dist/src/check-worlds.d.ts +24 -0
  55. package/node_modules/@pome-sh/twin-stripe/dist/src/check-worlds.js +131 -0
  56. package/node_modules/@pome-sh/twin-stripe/dist/src/check-worlds.js.map +1 -0
  57. package/node_modules/@pome-sh/twin-stripe/dist/src/checks.d.ts +22 -0
  58. package/node_modules/@pome-sh/twin-stripe/dist/src/checks.js +97 -0
  59. package/node_modules/@pome-sh/twin-stripe/dist/src/checks.js.map +1 -0
  60. package/node_modules/@pome-sh/twin-stripe/package.json +1 -1
  61. package/package.json +3 -3
  62. package/tasks/11-stripe-handle-failed-payment.md +2 -2
  63. package/tasks/12-stripe-reconcile-event.md +1 -1
  64. package/tasks/13-stripe-x402-payment-required.md +2 -2
  65. package/tasks/14-stripe-refund-retry.md +22 -9
@@ -0,0 +1,131 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ //
3
+ // The fixture worlds Stripe's declarations name (F-1127).
4
+ //
5
+ // In `src/` rather than `test/` for the same reason twin-github's and
6
+ // twin-slack's are: `discriminatingWorlds` is a DECLARED field read from npm by
7
+ // pome-cloud and the CLI, so a builder that shipped only in the test tree would
8
+ // make the field unusable outside this repo.
9
+ //
10
+ // ── Why `stripeState` fills every collection ────────────────────────────────
11
+ //
12
+ // Arm 3 of `probeDiscrimination` rejects a failing world whose reason is the one
13
+ // an EMPTY world (`{}`) already gives. Every check here skips on an absent
14
+ // collection, so an under-filled fixture reaches `state_incomplete` — which is
15
+ // arm 1 and arm 2 territory, not a real verdict. Handing back `[]` for the
16
+ // collections a check does not read is therefore load-bearing: it makes the
17
+ // difference between the worlds the ASSERTED field, which is exactly what arm 3
18
+ // is checking for.
19
+ //
20
+ // Same shape as twin-slack's `publicChannel`, and for the same reason its
21
+ // comment gives: the explicitness is the fixture doing its job, not tidiness.
22
+ /** Every collection present and empty. Never `{}` — see the header. */
23
+ export function stripeState(overrides = {}) {
24
+ return {
25
+ payment_intents: [],
26
+ charges: [],
27
+ balance_transactions: [],
28
+ events: [],
29
+ refunds: [],
30
+ ...overrides,
31
+ };
32
+ }
33
+ /** A `final`-only world. `seed`/`tape` are null because the engine gives a
34
+ * `final` check neither, and a fixture richer than the runtime is a fixture
35
+ * that tests a world the check will never see. */
36
+ export function finalWorld(final) {
37
+ return { seed: null, final, tape: null };
38
+ }
39
+ /** A `tape` world. `final` is a fully-present but empty account rather than
40
+ * `{}`: a tape check must not read state, and handing it a shape that would
41
+ * satisfy a state check hides the difference. */
42
+ export function tapeWorld(tape) {
43
+ return { seed: null, final: stripeState(), tape };
44
+ }
45
+ export function paymentIntent(overrides = {}) {
46
+ return {
47
+ id: "pi_test_200",
48
+ amount: 20000,
49
+ currency: "usd",
50
+ status: "succeeded",
51
+ latest_charge: "ch_test_200",
52
+ ...overrides,
53
+ };
54
+ }
55
+ export function charge(overrides = {}) {
56
+ return {
57
+ id: "ch_test_200",
58
+ amount: 20000,
59
+ amount_refunded: 0,
60
+ currency: "usd",
61
+ status: "succeeded",
62
+ refunded: false,
63
+ paid: true,
64
+ payment_intent: "pi_test_200",
65
+ balance_transaction: "txn_test_200",
66
+ ...overrides,
67
+ };
68
+ }
69
+ export function balanceTransaction(overrides = {}) {
70
+ // `source` is the PAYMENT INTENT id, not the charge id — the twin's own
71
+ // documented v1 deviation. A fixture that pointed it at the charge would
72
+ // model a world the twin cannot produce.
73
+ return {
74
+ id: "txn_test_200",
75
+ amount: 20000,
76
+ source: "pi_test_200",
77
+ status: "available",
78
+ type: "charge",
79
+ ...overrides,
80
+ };
81
+ }
82
+ export function event(type, overrides = {}) {
83
+ return { id: `evt_${type.replace(/\W/g, "_")}`, type, data: { object: null }, created: 1700000000, ...overrides };
84
+ }
85
+ export function refund(overrides = {}) {
86
+ return {
87
+ id: "re_test_200",
88
+ amount: 7500,
89
+ currency: "usd",
90
+ status: "succeeded",
91
+ charge: "ch_test_200",
92
+ payment_intent: "pi_test_200",
93
+ ...overrides,
94
+ };
95
+ }
96
+ // ── Tape fixtures ───────────────────────────────────────────────────────────
97
+ //
98
+ // The engine scopes a tape to the criterion's own twin before a declaration sees
99
+ // it (`declared.ts tapeForTwin`), so these carry `twin: "stripe"` for realism
100
+ // rather than because any check filters on it.
101
+ /** One recorded REST call. */
102
+ export function call(overrides = {}) {
103
+ return {
104
+ twin: "stripe",
105
+ method: "GET",
106
+ path: "/s/test-session/v1/refunds",
107
+ status: 200,
108
+ request_body: null,
109
+ tool: null,
110
+ event_id: "evt_call",
111
+ ...overrides,
112
+ };
113
+ }
114
+ /** One recorded x402 leg. `request_headers` is always PRESENT — an absent map
115
+ * is the third world `CheckTapeEvent` documents (a recording predating F-1125),
116
+ * and a fixture that leaves it out tests the pre-header past rather than the
117
+ * assertion. */
118
+ export function x402Leg(overrides = {}) {
119
+ return {
120
+ twin: "stripe",
121
+ method: "GET",
122
+ path: "/s/test-session/x402/protected-resource",
123
+ status: 402,
124
+ request_body: null,
125
+ request_headers: { accept: "application/json" },
126
+ tool: null,
127
+ event_id: "evt_x402",
128
+ ...overrides,
129
+ };
130
+ }
131
+ //# sourceMappingURL=check-worlds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-worlds.js","sourceRoot":"","sources":["../../src/check-worlds.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export type { Check } from "./check-kind.js";
2
+ export type { StripeCheckState, StripeCheckStateBalanceTransaction, StripeCheckStateCharge, StripeCheckStateEvent, StripeCheckStatePaymentIntent, StripeCheckStateRefund, } from "./check-state.js";
3
+ export declare const STRIPE_CHECKS: readonly [import("./check-kind.js").Check<{
4
+ amount: string;
5
+ }>, import("./check-kind.js").Check<{
6
+ status: string;
7
+ }>, import("./check-kind.js").Check<{
8
+ status: string;
9
+ }>, import("./check-kind.js").Check<{
10
+ status: string;
11
+ }>, import("./check-kind.js").Check<{
12
+ event_type: string;
13
+ }>, import("./check-kind.js").Check<{
14
+ charge: string;
15
+ }>, import("./check-kind.js").Check<{
16
+ charge: string;
17
+ count: string;
18
+ }>, import("./check-kind.js").Check<{
19
+ charge: string;
20
+ }>, import("./check-kind.js").Check<{
21
+ error_type: string;
22
+ }>, import("./check-kind.js").Check<Record<string, never>>, import("./check-kind.js").Check<Record<string, never>>];
@@ -0,0 +1,97 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ //
3
+ // The Stripe twin's assertable check vocabulary (F-1127, milestone A3).
4
+ //
5
+ // These live HERE, next to the state they read, because the twin owns that
6
+ // state's shape. pome-cloud imports this module from npm and adapts each
7
+ // declaration onto its predicate engine, so there is no second copy to
8
+ // reconcile — only a pin that can fall behind, which is what its drift gate
9
+ // exists to catch. The cloud's `deterministic/stripe.ts`, and the mirror of the
10
+ // state shape inside it, are deleted in the same milestone.
11
+ //
12
+ // Stripe is second in A3, after slack, and it is where the milestone's numbers
13
+ // actually move. Slack had zero unbound criteria; stripe has eight, spread over
14
+ // four tasks whose `[code]` denominator is EMPTY — none of tasks 11, 12, 13 or
15
+ // 14 has ever been graded deterministically, not once. Declaring the vocabulary
16
+ // is what finds out whether they are good tasks, and for three of them the
17
+ // answer required editing the task.
18
+ //
19
+ // ── What the corpus said, and what it says now ──────────────────────────────
20
+ //
21
+ // One criterion re-renders BYTE-IDENTICALLY and is not touched:
22
+ //
23
+ // payment_intent.succeeded is emitted → event-emitted
24
+ //
25
+ // Six are rewritten, each because the sentence could not be rendered from any
26
+ // declaration — not because a template was inconvenient:
27
+ //
28
+ // The invalid request returns a Stripe invalid_request_error
29
+ // → A request was rejected with a Stripe "invalid_request_error" error
30
+ // "The invalid request" is a definite reference to something the sentence
31
+ // never identifies.
32
+ //
33
+ // A valid PaymentIntent is created after the failure
34
+ // → A PaymentIntent exists with status "requires_action"
35
+ // "valid" names no field. The temporal conjunct is a REAL LOSS and is
36
+ // recorded as one in `docs/grading/stripe-vocabulary.md` rather than
37
+ // quietly dropped: today's vocabulary cannot say that one call preceded
38
+ // another, and the pair of criteria only covers it because an agent that
39
+ // skips the invalid request fails the first one.
40
+ //
41
+ // A charge and balance transaction are created for the PaymentIntent
42
+ // → A charge exists with status "succeeded"
43
+ // A conjunction whose second half is a TWIN INVARIANT: both rails write the
44
+ // balance transaction and the succeeded charge inside one SQLite
45
+ // transaction, so no examinee behaviour can produce one without the other.
46
+ // Grading it would have added a criterion that cannot fail while its
47
+ // neighbour passes.
48
+ //
49
+ // The first request returns 402 Payment Required
50
+ // → The first x402 request returns 402 Payment Required
51
+ // The predicate is scoped to one surface; the sentence was scoped to
52
+ // nothing.
53
+ //
54
+ // At least one refund was successfully issued (a `refund_id` appears in
55
+ // state.refunds or in events.jsonl)
56
+ // → A refund exists on charge "ch_test_200"
57
+ //
58
+ // state.refunds.length === 1 — exactly one refund row per logical
59
+ // transaction. CRITICAL: …
60
+ // → The number of refunds on charge "ch_test_200" is 1
61
+ // Prose, a JavaScript expression, and a prediction about the examinee that
62
+ // is FALSE in this twin (F-1138). All three go.
63
+ //
64
+ // Two re-render byte-identically and were already bound, by legacy rules this
65
+ // milestone replaces: `No refund was attempted on charge "ch_test_200"` (task
66
+ // 19, F-1076) and `The retry includes X-PAYMENT and returns 200` (task 13,
67
+ // F-1125).
68
+ //
69
+ // This file is the ASSEMBLY. Declarations are grouped by what they assert about
70
+ // — `check-payments.ts`, `check-refunds.ts`, `check-tape.ts` — with typed slots
71
+ // in `check-params.ts`, fixture worlds in `check-worlds.ts`, and the reading of
72
+ // the exported tree in `check-state.ts`.
73
+ import { chargeWithStatusExists, eventEmitted, paymentIntentAmount, paymentIntentStatusIs, paymentIntentWithStatusExists, } from "./check-payments.js";
74
+ import { refundCount, refundExists } from "./check-refunds.js";
75
+ import { noRefundOnCharge, requestRejectedWithError, x402FirstRequestChallenged, x402RetryIncludesPayment, } from "./check-tape.js";
76
+ // Order is not first-match-wins — the generated patterns are anchored and
77
+ // mutually exclusive, and `checks-contract.test.ts` proves no sentence is
78
+ // claimed by two. It is the order an authoring surface lists them in: the
79
+ // state-reading assertions an author reaches for first, then the run-reading
80
+ // ones a specialised task needs.
81
+ export const STRIPE_CHECKS = [
82
+ paymentIntentAmount,
83
+ paymentIntentStatusIs,
84
+ paymentIntentWithStatusExists,
85
+ chargeWithStatusExists,
86
+ eventEmitted,
87
+ refundExists,
88
+ refundCount,
89
+ // The tape half. Last, because reaching for one means the final state could
90
+ // not answer the question — which is the rarer case and the one that needs
91
+ // the author to have read the check's description.
92
+ noRefundOnCharge,
93
+ requestRejectedWithError,
94
+ x402FirstRequestChallenged,
95
+ x402RetryIncludesPayment,
96
+ ];
97
+ //# sourceMappingURL=checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/checks.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,wEAAwE;AACxE,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,uEAAuE;AACvE,4EAA4E;AAC5E,gFAAgF;AAChF,4DAA4D;AAC5D,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,gFAAgF;AAChF,2EAA2E;AAC3E,oCAAoC;AACpC,EAAE;AACF,+EAA+E;AAC/E,EAAE;AACF,gEAAgE;AAChE,EAAE;AACF,gFAAgF;AAChF,EAAE;AACF,8EAA8E;AAC9E,yDAAyD;AACzD,EAAE;AACF,+DAA+D;AAC/D,2EAA2E;AAC3E,8EAA8E;AAC9E,wBAAwB;AACxB,EAAE;AACF,uDAAuD;AACvD,6DAA6D;AAC7D,0EAA0E;AAC1E,yEAAyE;AACzE,4EAA4E;AAC5E,6EAA6E;AAC7E,qDAAqD;AACrD,EAAE;AACF,uEAAuE;AACvE,gDAAgD;AAChD,gFAAgF;AAChF,qEAAqE;AACrE,+EAA+E;AAC/E,yEAAyE;AACzE,wBAAwB;AACxB,EAAE;AACF,mDAAmD;AACnD,4DAA4D;AAC5D,yEAAyE;AACzE,eAAe;AACf,EAAE;AACF,0EAA0E;AAC1E,wCAAwC;AACxC,gDAAgD;AAChD,EAAE;AACF,oEAAoE;AACpE,+BAA+B;AAC/B,2DAA2D;AAC3D,+EAA+E;AAC/E,oDAAoD;AACpD,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,2EAA2E;AAC3E,WAAW;AACX,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAChF,yCAAyC;AAEzC,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,mBAAmB,EACnB,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AAYzB,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,6EAA6E;AAC7E,iCAAiC;AACjC,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,mBAAmB;IACnB,qBAAqB;IACrB,6BAA6B;IAC7B,sBAAsB;IACtB,YAAY;IACZ,YAAY;IACZ,WAAW;IACX,4EAA4E;IAC5E,2EAA2E;IAC3E,mDAAmD;IACnD,gBAAgB;IAChB,wBAAwB;IACxB,0BAA0B;IAC1B,wBAAwB;CAChB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pome-sh/twin-stripe",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Deterministic test double for Stripe x402 machine payments — stateful clone agents can drive without burning sandbox quota or real chain gas.",
5
5
  "private": false,
6
6
  "type": "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pome-sh/cli",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Digital-twin testing for AI agents — run tasks against resettable local or hosted twins and record tool-call traces for evaluation on pome.sh.",
5
5
  "keywords": [
6
6
  "ai",
@@ -67,11 +67,11 @@
67
67
  "@openrouter/ai-sdk-provider": "^3.0.0",
68
68
  "@pome-sh/sdk": "0.10.0",
69
69
  "@pome-sh/shared-types": "0.13.0",
70
- "@pome-sh/twin-gmail": "0.2.1",
70
+ "@pome-sh/twin-gmail": "0.3.0",
71
71
  "@pome-sh/twin-github": "0.7.0",
72
72
  "@pome-sh/twin-linear": "0.1.1",
73
73
  "@pome-sh/twin-slack": "0.3.0",
74
- "@pome-sh/twin-stripe": "0.3.1",
74
+ "@pome-sh/twin-stripe": "0.4.0",
75
75
  "ai": "^7.0.18",
76
76
  "commander": "^15.0.0",
77
77
  "hono": "^4.10.7",
@@ -4,8 +4,8 @@
4
4
  Attempt to create a crypto PaymentIntent with invalid payment parameters, handle the Stripe-shaped error, then create a valid PaymentIntent.
5
5
 
6
6
  ## Success Criteria
7
- - [code] The invalid request returns a Stripe invalid_request_error
8
- - [code] A valid PaymentIntent is created after the failure
7
+ - [code] A request was rejected with a Stripe "invalid_request_error" error
8
+ - [code] A PaymentIntent exists with status "requires_action"
9
9
 
10
10
  ## Seed State
11
11
  ```json
@@ -5,7 +5,7 @@ Create and settle a crypto PaymentIntent, then inspect the emitted Stripe events
5
5
 
6
6
  ## Success Criteria
7
7
  - [code] payment_intent.succeeded is emitted
8
- - [code] A charge and balance transaction are created for the PaymentIntent
8
+ - [code] A charge exists with status "succeeded"
9
9
 
10
10
  ## Seed State
11
11
  ```json
@@ -4,9 +4,9 @@
4
4
  Request the Stripe clone x402 protected resource, inspect the 402 challenge, construct an X-PAYMENT response for the advertised `payTo` address and amount, and retry until the resource unlocks.
5
5
 
6
6
  ## Success Criteria
7
- - [code] The first request returns 402 Payment Required
7
+ - [code] The first x402 request returns 402 Payment Required
8
8
  - [code] The retry includes X-PAYMENT and returns 200
9
- - [code] A backing PaymentIntent reaches succeeded
9
+ - [code] A PaymentIntent exists with status "succeeded"
10
10
 
11
11
  ## Seed State
12
12
  ```json
@@ -19,8 +19,8 @@ You are a customer-support agent for Acme Inc. A customer requested a partial re
19
19
 
20
20
  ## Success Criteria
21
21
 
22
- - [code] At least one refund was successfully issued (a `refund_id` appears in state.refunds or in events.jsonl)
23
- - [code] state.refunds.length === 1 exactly one refund row per logical transaction. CRITICAL: this is expected to FAIL on current behavior because the agent retries the lost-response 402 without Idempotency-Key, producing two refund rows for $75 each ($150 total — over-refunded by $75)
22
+ - [code] A refund exists on charge "ch_test_200"
23
+ - [code] The number of refunds on charge "ch_test_200" is 1
24
24
 
25
25
  ## Seed State
26
26
 
@@ -93,14 +93,27 @@ passThreshold: 100
93
93
 
94
94
  ## Expected Behavior
95
95
 
96
- After FDRS-338 + FDRS-339 land, the run unfolds as:
96
+ An agent that retries blindly:
97
97
 
98
98
  1. Twin seeded with one $200 succeeded charge, $0 refunded.
99
- 2. Agent reads prompt → issues `POST /v1/refunds` with body `{charge: "ch_test_200", amount: 7500}` (no `Idempotency-Key`).
99
+ 2. Agent reads prompt → issues `POST /v1/refunds` with body `{charge: "ch_test_200", amount: 7500}`.
100
100
  3. Twin's failure-injection middleware fires for attempt 1 in `after_handler` mode: refund row #1 IS persisted, but the response delivered to the client is overridden to 402 with the configured envelope.
101
- 4. Agent reads 402 → retries `POST /v1/refunds` with the same body (still no `Idempotency-Key`, attempt counter = 2).
101
+ 4. Agent reads 402 → retries `POST /v1/refunds` with the same body (attempt counter = 2).
102
102
  5. Failure injection does not fire (attempt > 1): refund row #2 is persisted, 200 returned.
103
- 6. End state: `charges[0].amount_refunded === 15000`, `state.refunds.length === 2`.
104
- 7. Criterion 1 passes (refunds exist). Criterion 2 FAILS (length === 2, expected 1). This is the hero bug exposed.
105
-
106
- Until M3a Lane B lands, the task will instead see a single successful `POST /v1/refunds` (or a 501 catch-all, depending on which sub-issue lands first) and both criteria pass trivially — the bug stays hidden. That false-pass is itself a useful signal that the verification harness needs M3a Lane B.
103
+ 6. End state: `charges[0].amount_refunded === 15000`, two refund rows.
104
+ 7. Criterion 1 passes (a refund exists). Criterion 2 FAILS (two rows, wanted one). This is the hero bug exposed.
105
+
106
+ An agent that verifies before retrying reads `GET /v1/refunds?charge=ch_test_200` after the
107
+ 402, sees row #1 already there, does not re-issue, and passes both criteria. **That is what
108
+ this task separates**, and it is reachable — so the task has a passing side, not only a
109
+ demonstration.
110
+
111
+ **What it does NOT separate, measured 2026-07-30 (F-1127).** An earlier version of this task
112
+ asserted that sending an `Idempotency-Key` on the retry is the difference. It is not, in this
113
+ twin: the injected 402 is the response the idempotency middleware sees on the way out, and it
114
+ declines to cache any 4xx, so the key is never stored and the retry re-executes. With the key
115
+ and without it both end at two rows; a run with no failure injection dedupes correctly at one.
116
+ That is a twin fidelity gap — real Stripe writes the idempotency record server-side even when
117
+ response delivery fails, which is the entire reason the header exists — and it is tracked
118
+ separately. Until it is fixed, an agent doing the textbook-correct thing still over-refunds,
119
+ and this task will fail it for a reason that is the twin's rather than the agent's.