@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,83 @@
|
|
|
1
|
+
export interface StripeCheckStatePaymentIntent {
|
|
2
|
+
id?: string;
|
|
3
|
+
amount?: number;
|
|
4
|
+
currency?: string;
|
|
5
|
+
status?: string;
|
|
6
|
+
latest_charge?: string | null;
|
|
7
|
+
}
|
|
8
|
+
export interface StripeCheckStateCharge {
|
|
9
|
+
id?: string;
|
|
10
|
+
amount?: number;
|
|
11
|
+
amount_refunded?: number;
|
|
12
|
+
currency?: string;
|
|
13
|
+
status?: string;
|
|
14
|
+
refunded?: boolean;
|
|
15
|
+
paid?: boolean;
|
|
16
|
+
payment_intent?: string | null;
|
|
17
|
+
balance_transaction?: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface StripeCheckStateBalanceTransaction {
|
|
20
|
+
id?: string;
|
|
21
|
+
amount?: number;
|
|
22
|
+
source?: string | null;
|
|
23
|
+
status?: string;
|
|
24
|
+
type?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface StripeCheckStateEvent {
|
|
27
|
+
id?: string;
|
|
28
|
+
type?: string;
|
|
29
|
+
data?: {
|
|
30
|
+
object?: unknown;
|
|
31
|
+
} | null;
|
|
32
|
+
created?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface StripeCheckStateRefund {
|
|
35
|
+
id?: string;
|
|
36
|
+
amount?: number;
|
|
37
|
+
currency?: string;
|
|
38
|
+
status?: string;
|
|
39
|
+
charge?: string | null;
|
|
40
|
+
payment_intent?: string | null;
|
|
41
|
+
}
|
|
42
|
+
export interface StripeCheckState {
|
|
43
|
+
payment_intents?: StripeCheckStatePaymentIntent[] | null;
|
|
44
|
+
charges?: StripeCheckStateCharge[] | null;
|
|
45
|
+
balance_transactions?: StripeCheckStateBalanceTransaction[] | null;
|
|
46
|
+
events?: StripeCheckStateEvent[] | null;
|
|
47
|
+
refunds?: StripeCheckStateRefund[] | null;
|
|
48
|
+
}
|
|
49
|
+
/** twin-github's and twin-slack's verdict type, same shape and same reason: a
|
|
50
|
+
* resolver must be able to say WHY it found nothing, because the ways of
|
|
51
|
+
* finding nothing get different verdicts. */
|
|
52
|
+
export type Resolved<T> = {
|
|
53
|
+
found: T;
|
|
54
|
+
} | {
|
|
55
|
+
missing: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The charge a criterion names, with the three outcomes a refund assertion has
|
|
59
|
+
* to tell apart:
|
|
60
|
+
* - no `charges` key at all → `state_incomplete`; nothing can be attested
|
|
61
|
+
* - present but this charge absent → `charge_not_found`. A SKIP rather than a
|
|
62
|
+
* verdict, on twin-slack's `resolveChannel` precedent: we cannot attest a
|
|
63
|
+
* positive over state we do not have, and for a negative a missing selector
|
|
64
|
+
* would hand a wrong agent a free pass.
|
|
65
|
+
* - found → the predicate evaluates. A present charge with zero refunds is a
|
|
66
|
+
* TRUE vacuous pass for a negative and a TRUE fail for a positive.
|
|
67
|
+
*
|
|
68
|
+
* Comparison is EXACT. A Stripe charge id is an opaque identifier the twin
|
|
69
|
+
* minted, not a human-typed name, so the case-insensitive `#`-tolerant matching
|
|
70
|
+
* twin-slack does for channel names would only widen what a typo can hit.
|
|
71
|
+
*/
|
|
72
|
+
export declare function resolveCharge(state: StripeCheckState, chargeId: string): Resolved<StripeCheckStateCharge>;
|
|
73
|
+
/**
|
|
74
|
+
* Every refund row against a charge, or the reason we cannot say.
|
|
75
|
+
*
|
|
76
|
+
* Resolves the CHARGE first and skips when it is missing, so "this charge has
|
|
77
|
+
* no refunds" and "there is no such charge" never reach the same verdict —
|
|
78
|
+
* they are the two answers a count assertion must not conflate.
|
|
79
|
+
*
|
|
80
|
+
* An EMPTY list is a real answer, deliberately distinct from either skip: a
|
|
81
|
+
* seeded charge nobody refunded really does have zero refunds.
|
|
82
|
+
*/
|
|
83
|
+
export declare function refundsOnCharge(state: StripeCheckState, chargeId: string): Resolved<StripeCheckStateRefund[]>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
//
|
|
3
|
+
// How a declared check READS the exported Stripe account (F-1127).
|
|
4
|
+
//
|
|
5
|
+
// pome-cloud kept a hand-maintained mirror of this shape in
|
|
6
|
+
// `deterministic/stripe.ts` (`StripeStateTree`, `StripePaymentIntent`,
|
|
7
|
+
// `StripeRefund`). That mirror is deleted in the same milestone; this is the
|
|
8
|
+
// only model, and it lives next to the export that produces it.
|
|
9
|
+
//
|
|
10
|
+
// ── One difference from Slack's model, and it changes every field name ──────
|
|
11
|
+
//
|
|
12
|
+
// Slack's `exportState()` spreads raw SQLite rows. Stripe's does NOT: every
|
|
13
|
+
// collection goes through `serializers.ts` first, so what a check reads is the
|
|
14
|
+
// STRIPE WIRE OBJECT, not the row. The join columns are the ones that move:
|
|
15
|
+
//
|
|
16
|
+
// table column wire field what a check must read
|
|
17
|
+
// refunds.charge_id refund.charge `charge`
|
|
18
|
+
// refunds.payment_intent_id refund.payment_intent
|
|
19
|
+
// charges.payment_intent_id charge.payment_intent
|
|
20
|
+
// charges.balance_transaction_id
|
|
21
|
+
// charge.balance_transaction
|
|
22
|
+
// balance_transactions.source_id
|
|
23
|
+
// balance_transaction.source
|
|
24
|
+
// payment_intents.latest_charge_id
|
|
25
|
+
// payment_intent.latest_charge
|
|
26
|
+
//
|
|
27
|
+
// A model written against the SEED schema (`seed.ts`) would carry the `_id`
|
|
28
|
+
// names and silently match nothing, which is a positive criterion failing a
|
|
29
|
+
// correct agent. The state-shape parity arm in `fidelity-contract.test.ts`
|
|
30
|
+
// asserts these names against a real `exportState()` rather than trusting this
|
|
31
|
+
// comment.
|
|
32
|
+
//
|
|
33
|
+
// Every field is optional and every list nullable, for twin-github's and
|
|
34
|
+
// twin-slack's reason: an older snapshot or a partial upload arrives here with
|
|
35
|
+
// fields absent, and a predicate that assumes presence throws inside the
|
|
36
|
+
// evaluator where a NAMED verdict was owed. A criterion may leave the
|
|
37
|
+
// denominator; it may never fabricate one (D4).
|
|
38
|
+
//
|
|
39
|
+
// Three shapes are counter-intuitive and each has a wrong-verdict story:
|
|
40
|
+
// - `charge.refunded` is true only when the charge is FULLY refunded. A
|
|
41
|
+
// partial refund leaves `amount_refunded > 0` and `refunded === false`
|
|
42
|
+
// (`serializers.ts:206-208`). A predicate reading `refunded` to mean "has
|
|
43
|
+
// any refund" misses every partial one — which is the only kind task 14
|
|
44
|
+
// produces.
|
|
45
|
+
// - `balance_transaction.source` points at the PAYMENT INTENT, not the
|
|
46
|
+
// charge, and the domain says so out loud: "real Stripe links the balance
|
|
47
|
+
// txn to the charge, not the PI. Keep it pointed at the PI for now … OK as
|
|
48
|
+
// v1 deviation" (`domain/stripe-domain.ts`, the crypto-deposit leg). A join
|
|
49
|
+
// written against upstream's semantics finds nothing here.
|
|
50
|
+
// - Every collection is exported `ORDER BY created DESC, rowid DESC` — NEWEST
|
|
51
|
+
// FIRST. `created` has unix-second resolution, so the rowid tiebreak is what
|
|
52
|
+
// makes it deterministic (F-683). Nothing in this vocabulary asserts on
|
|
53
|
+
// order, and that is deliberate: the ordered substrate is the TAPE, and a
|
|
54
|
+
// state check that leaned on export order would be reading an
|
|
55
|
+
// implementation detail the twin is free to change.
|
|
56
|
+
/**
|
|
57
|
+
* The charge a criterion names, with the three outcomes a refund assertion has
|
|
58
|
+
* to tell apart:
|
|
59
|
+
* - no `charges` key at all → `state_incomplete`; nothing can be attested
|
|
60
|
+
* - present but this charge absent → `charge_not_found`. A SKIP rather than a
|
|
61
|
+
* verdict, on twin-slack's `resolveChannel` precedent: we cannot attest a
|
|
62
|
+
* positive over state we do not have, and for a negative a missing selector
|
|
63
|
+
* would hand a wrong agent a free pass.
|
|
64
|
+
* - found → the predicate evaluates. A present charge with zero refunds is a
|
|
65
|
+
* TRUE vacuous pass for a negative and a TRUE fail for a positive.
|
|
66
|
+
*
|
|
67
|
+
* Comparison is EXACT. A Stripe charge id is an opaque identifier the twin
|
|
68
|
+
* minted, not a human-typed name, so the case-insensitive `#`-tolerant matching
|
|
69
|
+
* twin-slack does for channel names would only widen what a typo can hit.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveCharge(state, chargeId) {
|
|
72
|
+
if (state.charges == null)
|
|
73
|
+
return { missing: "state_incomplete" };
|
|
74
|
+
const hit = state.charges.find((charge) => charge.id === chargeId);
|
|
75
|
+
return hit ? { found: hit } : { missing: `charge_not_found ("${chargeId}")` };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Every refund row against a charge, or the reason we cannot say.
|
|
79
|
+
*
|
|
80
|
+
* Resolves the CHARGE first and skips when it is missing, so "this charge has
|
|
81
|
+
* no refunds" and "there is no such charge" never reach the same verdict —
|
|
82
|
+
* they are the two answers a count assertion must not conflate.
|
|
83
|
+
*
|
|
84
|
+
* An EMPTY list is a real answer, deliberately distinct from either skip: a
|
|
85
|
+
* seeded charge nobody refunded really does have zero refunds.
|
|
86
|
+
*/
|
|
87
|
+
export function refundsOnCharge(state, chargeId) {
|
|
88
|
+
const charge = resolveCharge(state, chargeId);
|
|
89
|
+
if ("missing" in charge)
|
|
90
|
+
return charge;
|
|
91
|
+
if (state.refunds == null)
|
|
92
|
+
return { missing: "state_incomplete" };
|
|
93
|
+
return { found: state.refunds.filter((refund) => refund.charge === chargeId) };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=check-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-state.js","sourceRoot":"","sources":["../../src/check-state.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,EAAE;AACF,mEAAmE;AACnE,EAAE;AACF,4DAA4D;AAC5D,uEAAuE;AACvE,6EAA6E;AAC7E,gEAAgE;AAChE,EAAE;AACF,+EAA+E;AAC/E,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,4EAA4E;AAC5E,EAAE;AACF,0EAA0E;AAC1E,4DAA4D;AAC5D,oDAAoD;AACpD,oDAAoD;AACpD,mCAAmC;AACnC,yDAAyD;AACzD,mCAAmC;AACnC,yDAAyD;AACzD,qCAAqC;AACrC,2DAA2D;AAC3D,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,+EAA+E;AAC/E,WAAW;AACX,EAAE;AACF,yEAAyE;AACzE,+EAA+E;AAC/E,yEAAyE;AACzE,sEAAsE;AACtE,gDAAgD;AAChD,EAAE;AACF,yEAAyE;AACzE,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,4EAA4E;AAC5E,gBAAgB;AAChB,yEAAyE;AACzE,8EAA8E;AAC9E,gFAAgF;AAChF,gFAAgF;AAChF,+DAA+D;AAC/D,gFAAgF;AAChF,iFAAiF;AACjF,4EAA4E;AAC5E,8EAA8E;AAC9E,kEAAkE;AAClE,wDAAwD;AA2ExD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAuB,EACvB,QAAgB;IAEhB,IAAI,KAAK,CAAC,OAAO,IAAI,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAClE,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IACnE,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,sBAAsB,QAAQ,IAAI,EAAE,CAAC;AAChF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAuB,EACvB,QAAgB;IAEhB,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC9C,IAAI,SAAS,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IACvC,IAAI,KAAK,CAAC,OAAO,IAAI,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAClE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,EAAE,CAAC;AACjF,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Check } from "./check-kind.js";
|
|
2
|
+
export declare const noRefundOnCharge: Check<{
|
|
3
|
+
charge: string;
|
|
4
|
+
}>;
|
|
5
|
+
export declare const requestRejectedWithError: Check<{
|
|
6
|
+
error_type: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const x402FirstRequestChallenged: Check<Record<string, never>>;
|
|
9
|
+
export declare const x402RetryIncludesPayment: Check<Record<string, never>>;
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
//
|
|
3
|
+
// What Stripe's declared checks can assert about the RUN — the recorded call
|
|
4
|
+
// tape rather than the exported end state (F-1127).
|
|
5
|
+
//
|
|
6
|
+
// Four checks, and every one of them exists because the final state cannot
|
|
7
|
+
// answer the question:
|
|
8
|
+
//
|
|
9
|
+
// no-refund-on-charge the twin REFUSES a second refund on an
|
|
10
|
+
// already-refunded charge, so the attempt never
|
|
11
|
+
// reaches `state.refunds`
|
|
12
|
+
// request-rejected-with-error a rejected request mutates nothing, so an error
|
|
13
|
+
// the agent provoked and handled is invisible in
|
|
14
|
+
// the end state
|
|
15
|
+
// x402-first-request-challenged a 402 challenge mutates nothing
|
|
16
|
+
// x402-retry-includes-payment the challenge leg and the paid leg are the
|
|
17
|
+
// SAME method on the SAME path; only their headers
|
|
18
|
+
// tell them apart
|
|
19
|
+
//
|
|
20
|
+
// ── The twin filter is GONE, and that is not an omission ────────────────────
|
|
21
|
+
//
|
|
22
|
+
// The legacy rules in pome-cloud each opened with
|
|
23
|
+
// `(event.twin ?? "stripe") === "stripe"`, because a legacy `PhraseRule` was
|
|
24
|
+
// handed the whole session's tape. A declaration is not: the engine scopes the
|
|
25
|
+
// tape to the criterion's own twin before `evaluate` runs
|
|
26
|
+
// (`declared.ts tapeForTwin`), precisely so a twin's check need not know that
|
|
27
|
+
// multi-twin sessions exist and a new tape check cannot forget to. Re-filtering
|
|
28
|
+
// here would be a second copy of that decision, and the copy is the one that
|
|
29
|
+
// goes stale.
|
|
30
|
+
//
|
|
31
|
+
// ── `null` and `[]` are different worlds ────────────────────────────────────
|
|
32
|
+
//
|
|
33
|
+
// `null` is "nobody handed me a tape"; `[]` is "the agent called nothing". Every
|
|
34
|
+
// check below guards `null` explicitly even though the engine guards it too, so
|
|
35
|
+
// a consumer that forgets produces a named skip rather than a vacuous pass. For
|
|
36
|
+
// a NEGATIVE criterion that distinction is the whole ballgame.
|
|
37
|
+
import { defineCheck, VACUITY_SENTINEL } from "@pome-sh/sdk/checks";
|
|
38
|
+
import { chargeId, stripeErrorType } from "./check-params.js";
|
|
39
|
+
import { call, tapeWorld, x402Leg } from "./check-worlds.js";
|
|
40
|
+
const TAPE_MISSING = { passed: false, status: "skipped", reason: "tape_missing" };
|
|
41
|
+
/** The recorded ids backing an outcome, minus the rows that carry none. Losing
|
|
42
|
+
* an id must never lose a finding, so this narrows the CITATION and never the
|
|
43
|
+
* count or the prose (F-980). */
|
|
44
|
+
function citations(events) {
|
|
45
|
+
return events
|
|
46
|
+
.map((event) => event.event_id)
|
|
47
|
+
.filter((id) => typeof id === "string" && id !== "");
|
|
48
|
+
}
|
|
49
|
+
/** Attach citations only when there are some: absent and `[]` mean the same
|
|
50
|
+
* thing downstream, and an empty affordance would have to be special-cased by
|
|
51
|
+
* every reader of the persisted row. */
|
|
52
|
+
function withCitations(outcome, events) {
|
|
53
|
+
const ids = citations(events);
|
|
54
|
+
return ids.length > 0 ? { ...outcome, evidenceEventIds: ids } : outcome;
|
|
55
|
+
}
|
|
56
|
+
/** The `error.type` a recorded response carries, or null.
|
|
57
|
+
*
|
|
58
|
+
* `response_body` is `unknown` on the wire — stripe records the parsed object,
|
|
59
|
+
* but another recorder may hand back the raw text — so this reads both rather
|
|
60
|
+
* than assuming. Reading only objects would silently miss the rejection, and
|
|
61
|
+
* for a POSITIVE criterion a silent miss fails a correct agent. */
|
|
62
|
+
function recordedErrorType(body) {
|
|
63
|
+
const parsed = typeof body === "string"
|
|
64
|
+
? (() => {
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(body);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
})()
|
|
72
|
+
: body;
|
|
73
|
+
if (parsed === null || typeof parsed !== "object")
|
|
74
|
+
return null;
|
|
75
|
+
const error = parsed.error;
|
|
76
|
+
if (error === null || typeof error !== "object")
|
|
77
|
+
return null;
|
|
78
|
+
const type = error.type;
|
|
79
|
+
return typeof type === "string" ? type : null;
|
|
80
|
+
}
|
|
81
|
+
/** The x402 legs on a tape, in order. Scoped by PATH because the gated surface
|
|
82
|
+
* is mounted at `/x402/*` (`session.ts`) and the recorded path carries the
|
|
83
|
+
* session prefix — `/s/<sid>/x402/protected-resource`. The twin's own settlement
|
|
84
|
+
* calls (`POST /v1/payment_intents`, `simulate_crypto_deposit`) travel back over
|
|
85
|
+
* HTTP and are recorded by the REST routes that serve them, so they are NOT
|
|
86
|
+
* x402 legs and must not be counted as ones. */
|
|
87
|
+
function x402Legs(tape) {
|
|
88
|
+
return tape.filter((event) => (event.path ?? "").includes("/x402/"));
|
|
89
|
+
}
|
|
90
|
+
export const noRefundOnCharge = defineCheck({
|
|
91
|
+
id: "stripe.no-refund-on-charge",
|
|
92
|
+
description: "Scans the recorded call tape for a `POST /v1/refunds` whose request body names this " +
|
|
93
|
+
"charge, and fails if one exists. It counts the ATTEMPT, not the outcome: a refund the twin " +
|
|
94
|
+
"REJECTED still called for one, and rejecting an attempt is not the same as not making it. " +
|
|
95
|
+
"That is the whole reason this reads the tape — the twin refuses a second refund on an " +
|
|
96
|
+
"already-refunded charge, so no examinee behaviour can put a matching row in " +
|
|
97
|
+
"`state.refunds`, and read against state the criterion could never fail. A GET that merely " +
|
|
98
|
+
"LISTS refunds is not an attempt and does not fail it.",
|
|
99
|
+
template: 'No refund was attempted on charge "{charge}"',
|
|
100
|
+
params: { charge: chargeId },
|
|
101
|
+
substrate: "tape",
|
|
102
|
+
polarity: () => "negative",
|
|
103
|
+
// The charge id IS the scanned literal here — hunted for inside a recorded
|
|
104
|
+
// request body, with nothing to resolve it against. That is what earns it both
|
|
105
|
+
// a real `subject` and a real mutant, where the state-reading refund checks
|
|
106
|
+
// (`check-refunds.ts`) get neither.
|
|
107
|
+
subject: (args) => args.charge,
|
|
108
|
+
vacuityMutant: (args) => ({ ...args, charge: VACUITY_SENTINEL }),
|
|
109
|
+
discriminatingWorlds: (args) => ({
|
|
110
|
+
// A non-empty PASSING tape, on purpose: an empty tape passes too, and a
|
|
111
|
+
// fixture that used one would demonstrate nothing about the check. This one
|
|
112
|
+
// shows the distinction the description makes — a GET that lists refunds for
|
|
113
|
+
// the very same charge is not an attempt.
|
|
114
|
+
passing: tapeWorld([
|
|
115
|
+
call({ method: "GET", path: `/s/test-session/v1/refunds?charge=${args.charge}`, status: 200 }),
|
|
116
|
+
]),
|
|
117
|
+
failing: tapeWorld([
|
|
118
|
+
call({
|
|
119
|
+
method: "POST",
|
|
120
|
+
path: "/s/test-session/v1/refunds",
|
|
121
|
+
status: 400,
|
|
122
|
+
request_body: JSON.stringify({ charge: args.charge, amount: 20000 }),
|
|
123
|
+
event_id: "evt_attempt",
|
|
124
|
+
}),
|
|
125
|
+
]),
|
|
126
|
+
}),
|
|
127
|
+
evaluate(args, { tape }) {
|
|
128
|
+
if (tape === null)
|
|
129
|
+
return TAPE_MISSING;
|
|
130
|
+
const attempts = tape.filter((event) => {
|
|
131
|
+
if ((event.method ?? "").toUpperCase() !== "POST")
|
|
132
|
+
return false;
|
|
133
|
+
if (!(event.path ?? "").includes("/v1/refunds"))
|
|
134
|
+
return false;
|
|
135
|
+
// The charge id lives in the REQUEST BODY, which is why this needs the
|
|
136
|
+
// widened substrate rather than method+path. stripe records the raw text;
|
|
137
|
+
// another twin may record an object, and reading only strings would
|
|
138
|
+
// silently miss the attempt.
|
|
139
|
+
const body = event.request_body;
|
|
140
|
+
const haystack = typeof body === "string" ? body : JSON.stringify(body ?? "");
|
|
141
|
+
return haystack.includes(args.charge);
|
|
142
|
+
});
|
|
143
|
+
if (attempts.length === 0) {
|
|
144
|
+
return {
|
|
145
|
+
passed: true,
|
|
146
|
+
reason: `no refund was attempted on charge "${args.charge}" (${tape.length} call(s) inspected)`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return withCitations({
|
|
150
|
+
passed: false,
|
|
151
|
+
reason: `${attempts.length} refund attempt(s) on charge "${args.charge}" ` +
|
|
152
|
+
`(statuses: [${attempts.map((event) => event.status ?? "?").join(", ")}]) — ` +
|
|
153
|
+
`the twin rejecting an attempt is not the same as not attempting one`,
|
|
154
|
+
}, attempts);
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
export const requestRejectedWithError = defineCheck({
|
|
158
|
+
id: "stripe.request-rejected-with-error",
|
|
159
|
+
description: "Scans the recorded call tape for a response carrying Stripe's error envelope with this " +
|
|
160
|
+
"`error.type`, and passes if one exists. It asserts the agent PROVOKED and received that " +
|
|
161
|
+
"class of error — which mutates nothing, so the end state cannot show it — and asserts " +
|
|
162
|
+
"nothing about whether the agent then handled it, or about the error's `code`. It reads " +
|
|
163
|
+
"`error.type` on the response body rather than the HTTP status, because one status carries " +
|
|
164
|
+
"several types: a 400 may be `invalid_request_error` or `idempotency_error`, and a card " +
|
|
165
|
+
"decline is a 402 `card_error`.",
|
|
166
|
+
template: 'A request was rejected with a Stripe "{error_type}" error',
|
|
167
|
+
params: { error_type: stripeErrorType },
|
|
168
|
+
substrate: "tape",
|
|
169
|
+
polarity: () => "positive",
|
|
170
|
+
// Closed set of five — the twin's own `StripeErrorType` union — so no member
|
|
171
|
+
// is guaranteed false. Admitted in `HONEST_NULL_MUTANTS`.
|
|
172
|
+
vacuityMutant: () => null,
|
|
173
|
+
discriminatingWorlds: ({ error_type }) => {
|
|
174
|
+
const other = error_type === "card_error" ? "api_error" : "card_error";
|
|
175
|
+
return {
|
|
176
|
+
passing: tapeWorld([
|
|
177
|
+
call({ method: "POST", path: "/s/test-session/v1/payment_intents", status: 200 }),
|
|
178
|
+
call({
|
|
179
|
+
method: "POST",
|
|
180
|
+
path: "/s/test-session/v1/payment_intents",
|
|
181
|
+
status: 400,
|
|
182
|
+
response_body: { error: { type: error_type, code: "parameter_invalid" } },
|
|
183
|
+
event_id: "evt_rejected",
|
|
184
|
+
}),
|
|
185
|
+
]),
|
|
186
|
+
// A request that WAS rejected, with a different type. A clean tape would
|
|
187
|
+
// fail for the reason an empty one does; this fails because the agent
|
|
188
|
+
// provoked the wrong error, which is the assertion.
|
|
189
|
+
failing: tapeWorld([
|
|
190
|
+
call({
|
|
191
|
+
method: "POST",
|
|
192
|
+
path: "/s/test-session/v1/payment_intents",
|
|
193
|
+
status: 400,
|
|
194
|
+
response_body: { error: { type: other, code: "parameter_invalid" } },
|
|
195
|
+
}),
|
|
196
|
+
]),
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
evaluate({ error_type }, { tape }) {
|
|
200
|
+
if (tape === null)
|
|
201
|
+
return TAPE_MISSING;
|
|
202
|
+
const typed = tape
|
|
203
|
+
.map((event) => ({ event, type: recordedErrorType(event.response_body) }))
|
|
204
|
+
.filter((row) => row.type !== null);
|
|
205
|
+
const hits = typed.filter((row) => row.type === error_type);
|
|
206
|
+
if (hits.length === 0) {
|
|
207
|
+
return {
|
|
208
|
+
passed: false,
|
|
209
|
+
reason: `no recorded call was rejected with a Stripe "${error_type}" error ` +
|
|
210
|
+
`(${tape.length} call(s) inspected; error types seen: ` +
|
|
211
|
+
`[${typed.map((row) => row.type).join(", ")}])`,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return withCitations({
|
|
215
|
+
passed: true,
|
|
216
|
+
reason: `${hits.length} call(s) were rejected with a Stripe "${error_type}" error ` +
|
|
217
|
+
`(${tape.length} call(s) inspected)`,
|
|
218
|
+
}, hits.map((row) => row.event));
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
export const x402FirstRequestChallenged = defineCheck({
|
|
222
|
+
id: "stripe.x402-first-request-challenged",
|
|
223
|
+
description: "Finds the recorded calls to the x402 protected resource, in order, and asserts the FIRST " +
|
|
224
|
+
"one was answered 402 Payment Required. It is about the challenge leg specifically, not " +
|
|
225
|
+
"about any 402 anywhere — a card decline is also a 402 and does not satisfy it. The twin's " +
|
|
226
|
+
"own settlement calls travel back over HTTP as ordinary `/v1/*` REST and are not counted as " +
|
|
227
|
+
"x402 legs. A run that never touched the protected resource FAILS rather than skipping: the " +
|
|
228
|
+
"task asks the agent to request it, and never asking is the failure.",
|
|
229
|
+
// The sentence names x402 where the corpus said only "The first request".
|
|
230
|
+
// A rendered sentence must not be wider than the predicate — the predicate is
|
|
231
|
+
// scoped to one surface and the old wording was scoped to nothing, which is
|
|
232
|
+
// the readable-surface-hides-the-disagreement defect the contract test's
|
|
233
|
+
// description arm exists for.
|
|
234
|
+
template: "The first x402 request returns 402 Payment Required",
|
|
235
|
+
params: {},
|
|
236
|
+
substrate: "tape",
|
|
237
|
+
// Nothing in the seed satisfies it and only the examinee acting can, so it
|
|
238
|
+
// should FAIL on the seed.
|
|
239
|
+
polarity: () => "positive",
|
|
240
|
+
// No slots at all, so there is no literal to falsify — the trigger is a status
|
|
241
|
+
// code on a path, which lives on the tape and not in the sentence. Reported as
|
|
242
|
+
// `no_trigger`, never as clean.
|
|
243
|
+
subject: () => null,
|
|
244
|
+
vacuityMutant: () => null,
|
|
245
|
+
discriminatingWorlds: () => ({
|
|
246
|
+
passing: tapeWorld([x402Leg({ status: 402, event_id: "evt_challenge" }), x402Leg({ status: 200 })]),
|
|
247
|
+
// The resource WAS requested and answered 200 first — an ungated surface.
|
|
248
|
+
// A tape with no x402 leg at all would fail the way an empty one does.
|
|
249
|
+
failing: tapeWorld([x402Leg({ status: 200 }), x402Leg({ status: 200 })]),
|
|
250
|
+
}),
|
|
251
|
+
evaluate(_args, { tape }) {
|
|
252
|
+
if (tape === null)
|
|
253
|
+
return TAPE_MISSING;
|
|
254
|
+
const legs = x402Legs(tape);
|
|
255
|
+
if (legs.length === 0) {
|
|
256
|
+
return {
|
|
257
|
+
passed: false,
|
|
258
|
+
reason: `no request to the x402 protected resource was recorded (${tape.length} call(s) inspected)`,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const first = legs[0];
|
|
262
|
+
const challenged = first.status === 402;
|
|
263
|
+
return withCitations({
|
|
264
|
+
passed: challenged,
|
|
265
|
+
reason: challenged
|
|
266
|
+
? `the first of ${legs.length} x402 request(s) returned 402 Payment Required`
|
|
267
|
+
: `the first of ${legs.length} x402 request(s) returned ${first.status ?? "?"}, not 402`,
|
|
268
|
+
}, [first]);
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
export const x402RetryIncludesPayment = defineCheck({
|
|
272
|
+
id: "stripe.x402-retry-includes-payment",
|
|
273
|
+
description: "Asserts some recorded request carried an `X-PAYMENT` header AND was answered 200 — the " +
|
|
274
|
+
"agent constructed a payment for the advertised challenge and the resource unlocked. Both " +
|
|
275
|
+
"halves matter: a header the twin refused is a different failure from never sending one, and " +
|
|
276
|
+
"the reason says which. It reads the header case-insensitively, because the runtime " +
|
|
277
|
+
"lowercases keys but a tape from another recorder may preserve what the agent sent. A " +
|
|
278
|
+
"recording made before headers existed is a SKIP, never a verdict.",
|
|
279
|
+
template: "The retry includes X-PAYMENT and returns 200",
|
|
280
|
+
params: {},
|
|
281
|
+
substrate: "tape",
|
|
282
|
+
polarity: () => "positive",
|
|
283
|
+
// The scanned literal is the HEADER NAME, fixed by the sentence rather than
|
|
284
|
+
// supplied by an author — there is no slot to falsify. Nothing here is a
|
|
285
|
+
// caller-supplied literal a redactor could delete either. (The header VALUE is
|
|
286
|
+
// base64 JSON, always beginning `eyJ` and one character class away from the
|
|
287
|
+
// JWT scrubber; that it survives redaction is asserted in
|
|
288
|
+
// `packages/sdk/test/redaction.test.ts`, because a scrubbed subject would make
|
|
289
|
+
// this permanently unanswerable in a way nothing here could detect.)
|
|
290
|
+
subject: () => null,
|
|
291
|
+
vacuityMutant: () => null,
|
|
292
|
+
discriminatingWorlds: () => ({
|
|
293
|
+
passing: tapeWorld([
|
|
294
|
+
x402Leg({ status: 402 }),
|
|
295
|
+
x402Leg({
|
|
296
|
+
status: 200,
|
|
297
|
+
request_headers: { "x-payment": "eyJzY2hlbWUiOiJleGFjdCJ9" },
|
|
298
|
+
event_id: "evt_paid",
|
|
299
|
+
}),
|
|
300
|
+
]),
|
|
301
|
+
// A payment the twin REFUSED. The agent did construct one, so this is not
|
|
302
|
+
// the never-tried world an empty tape gives.
|
|
303
|
+
failing: tapeWorld([
|
|
304
|
+
x402Leg({ status: 402 }),
|
|
305
|
+
x402Leg({ status: 402, request_headers: { "x-payment": "eyJzY2hlbWUiOiJ3cm9uZyJ9" } }),
|
|
306
|
+
]),
|
|
307
|
+
}),
|
|
308
|
+
evaluate(_args, { tape }) {
|
|
309
|
+
if (tape === null)
|
|
310
|
+
return TAPE_MISSING;
|
|
311
|
+
// A tape recorded before F-1125 carries no headers at all, and this turns
|
|
312
|
+
// entirely on one. "No row had X-PAYMENT" and "no row COULD have had it" are
|
|
313
|
+
// the same absence, so answering would be inventing a verdict — and because
|
|
314
|
+
// this criterion is POSITIVE, the invented verdict fails a correct agent.
|
|
315
|
+
// Name it instead.
|
|
316
|
+
if (tape.length > 0 && !tape.some((event) => event.request_headers !== undefined)) {
|
|
317
|
+
return { passed: false, status: "skipped", reason: "headers_not_recorded" };
|
|
318
|
+
}
|
|
319
|
+
const carriesPayment = (event) => Object.keys(event.request_headers ?? {}).some((key) => key.toLowerCase() === "x-payment");
|
|
320
|
+
const unlocked = tape.filter((event) => carriesPayment(event) && event.status === 200);
|
|
321
|
+
if (unlocked.length === 0) {
|
|
322
|
+
const attempted = tape.filter(carriesPayment);
|
|
323
|
+
// Say WHICH half is missing. "Never sent one" and "sent one and the twin
|
|
324
|
+
// refused it" are different failures wanting different fixes.
|
|
325
|
+
const detail = attempted.length === 0
|
|
326
|
+
? "no recorded request carried an X-PAYMENT header"
|
|
327
|
+
: `${attempted.length} request(s) carried X-PAYMENT but none returned 200 ` +
|
|
328
|
+
`(statuses: [${attempted.map((event) => event.status ?? "?").join(", ")}])`;
|
|
329
|
+
return { passed: false, reason: `${detail} (${tape.length} call(s) inspected)` };
|
|
330
|
+
}
|
|
331
|
+
return withCitations({
|
|
332
|
+
passed: true,
|
|
333
|
+
reason: `${unlocked.length} request(s) carried X-PAYMENT and returned 200 ` +
|
|
334
|
+
`(${tape.length} call(s) inspected)`,
|
|
335
|
+
}, unlocked);
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
//# sourceMappingURL=check-tape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-tape.js","sourceRoot":"","sources":["../../src/check-tape.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CheckSubstrate, CheckTapeEvent } from "@pome-sh/sdk/checks";
|
|
2
|
+
import type { StripeCheckState, StripeCheckStateBalanceTransaction, StripeCheckStateCharge, StripeCheckStateEvent, StripeCheckStatePaymentIntent, StripeCheckStateRefund } from "./check-state.js";
|
|
3
|
+
/** Every collection present and empty. Never `{}` — see the header. */
|
|
4
|
+
export declare function stripeState(overrides?: StripeCheckState): StripeCheckState;
|
|
5
|
+
/** A `final`-only world. `seed`/`tape` are null because the engine gives a
|
|
6
|
+
* `final` check neither, and a fixture richer than the runtime is a fixture
|
|
7
|
+
* that tests a world the check will never see. */
|
|
8
|
+
export declare function finalWorld(final: StripeCheckState): CheckSubstrate<StripeCheckState>;
|
|
9
|
+
/** A `tape` world. `final` is a fully-present but empty account rather than
|
|
10
|
+
* `{}`: a tape check must not read state, and handing it a shape that would
|
|
11
|
+
* satisfy a state check hides the difference. */
|
|
12
|
+
export declare function tapeWorld(tape: readonly CheckTapeEvent[]): CheckSubstrate<StripeCheckState>;
|
|
13
|
+
export declare function paymentIntent(overrides?: Partial<StripeCheckStatePaymentIntent>): StripeCheckStatePaymentIntent;
|
|
14
|
+
export declare function charge(overrides?: Partial<StripeCheckStateCharge>): StripeCheckStateCharge;
|
|
15
|
+
export declare function balanceTransaction(overrides?: Partial<StripeCheckStateBalanceTransaction>): StripeCheckStateBalanceTransaction;
|
|
16
|
+
export declare function event(type: string, overrides?: Partial<StripeCheckStateEvent>): StripeCheckStateEvent;
|
|
17
|
+
export declare function refund(overrides?: Partial<StripeCheckStateRefund>): StripeCheckStateRefund;
|
|
18
|
+
/** One recorded REST call. */
|
|
19
|
+
export declare function call(overrides?: Partial<CheckTapeEvent>): CheckTapeEvent;
|
|
20
|
+
/** One recorded x402 leg. `request_headers` is always PRESENT — an absent map
|
|
21
|
+
* is the third world `CheckTapeEvent` documents (a recording predating F-1125),
|
|
22
|
+
* and a fixture that leaves it out tests the pre-header past rather than the
|
|
23
|
+
* assertion. */
|
|
24
|
+
export declare function x402Leg(overrides?: Partial<CheckTapeEvent>): CheckTapeEvent;
|