@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.
- package/dist/build-info.json +3 -3
- package/dist/src/cli/checks.d.ts +14 -0
- package/dist/src/cli/checks.js +27 -3
- package/node_modules/@pome-sh/twin-gmail/CHANGELOG.md +39 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.d.ts +7 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js +138 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-kind.d.ts +3 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-kind.js +9 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-kind.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.d.ts +4 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js +56 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.d.ts +14 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js +207 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-params.d.ts +10 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-params.js +96 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-params.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.d.ts +113 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js +145 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-tape.d.ts +2 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-tape.js +103 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-tape.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-worlds.d.ts +17 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-worlds.js +62 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-worlds.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/checks.d.ts +19 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/checks.js +42 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/checks.js.map +1 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/index.d.ts +2 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/index.js +4 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/index.js.map +1 -1
- package/node_modules/@pome-sh/twin-gmail/package.json +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-kind.d.ts +3 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-kind.js +9 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-kind.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-params.d.ts +8 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-params.js +123 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-params.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.d.ts +18 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js +264 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.d.ts +8 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js +122 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.d.ts +83 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js +95 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-tape.d.ts +9 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-tape.js +338 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-tape.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-worlds.d.ts +24 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-worlds.js +131 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-worlds.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/checks.d.ts +22 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/checks.js +97 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/checks.js.map +1 -0
- package/node_modules/@pome-sh/twin-stripe/package.json +1 -1
- package/package.json +3 -3
- package/tasks/11-stripe-handle-failed-payment.md +2 -2
- package/tasks/12-stripe-reconcile-event.md +1 -1
- package/tasks/13-stripe-x402-payment-required.md +2 -2
- 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
|
+
"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.
|
|
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.
|
|
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.
|
|
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]
|
|
8
|
-
- [code] A
|
|
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
|
|
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
|
|
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]
|
|
23
|
-
- [code]
|
|
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
|
-
|
|
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}
|
|
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 (
|
|
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`,
|
|
104
|
-
7. Criterion 1 passes (
|
|
105
|
-
|
|
106
|
-
|
|
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.
|