TEST REPORT: Stripe Endive Upgrade Check v1.0.1
Run from a clean unzip of the v1.0.1 zip, as built before this report was added: stripe-endive-upgrade-check-v1.0.1-without-report.zip, sha256 c66c7b55a514fe4453971e25859ff01c774f91ef50d76a25142b962a3586b800, 39 files. The shipped stripe-endive-upgrade-check-v1.0.1.zip is that build plus this TEST-REPORT.md; every other file is byte-identical (checked with diff -rq when the shipped zip was built).
Run by staging/new-packs-20261023/build_and_test.sh stripe-endive-upgrade-check pre (with VER=1.0.1), which runs tests/run_all.sh. This is the second of two identical clean runs. Started 2026-10-23 19:51:21 BST (UTC+01:00), finished 2026-10-23 19:51:35 BST (UTC+01:00) (times from date at run time). The first run (19:51:06–19:51:21 BST) gave the same output line for line, apart from times and the temp folder. Box: Linux 6.12.94+, Python 3.13.5, Node v24.22.0, npm 11.19.1. SDKs installed fresh from npm and PyPI for the run: stripe-node 22.6.2 and 23.0.1, stripe-python 16.0.0, TypeScript 5.9.3.
Result: all of these passed, and FAILED STEPS: 0:
- 27/27 scanner unit tests (15 from v1.0.0 plus 12 new).
- 8/8 skill structure checks.
- The example scan (2/2 exit codes).
- 3/3 TypeScript compiles, with the exact expected error set.
- 23/23 stripe-node probe checks.
- 15/15 stripe-python probe checks.
Not run inside Claude Code or Cursor
This skill was NOT run inside a live Claude Code or Cursor session. Both need a signed-in account (Claude Code's claude -p answered "Not logged in · Please run /login"; Cursor's CLI needs agent login or an API key), and none was available. So no AI agent has followed these instructions yet. These substitute checks did run:
- Everything in sections 1 to 5 below. That covers the scanner, the skill's static structure checks, a TypeScript compile against the real stripe-node types, and probes of the real stripe-node and stripe-python packages against a local server.
- The manual path an agent would take, in a project install. The v1.0.1 build was installed at
.cursor/skills/, at.claude/skills/, and at both at once, in a copy of the before-app. Then the exact command from SKILL.md step 1 was run from the repo root. Each time the result was 15 blockers, 4 warnings and 6 info, the same as the before-app alone, with 0 findings from the skill's folders. The fixed after-app with both copies installed gave--strictexit 0. - The SKILL.md frontmatter and folder layout, checked against the current Claude Code, Cursor and agentskills.io docs.
claude plugin validate .claude/skills(Claude Code 2.1.309 CLI; no sign-in needed). It passed. This is weak evidence: in our checks it also passed a file with broken YAML.
What changed in v1.0.1 (see CHANGELOG.md)
In v1.0.0, with the skill installed inside a project, scanning the repo root (SKILL.md step 1) also scanned the skill's own fixtures, tests and scanner. Those fixtures contain broken Stripe code on purpose. With one copy installed, 48 of 73 findings came from the skill itself; with two copies, 96 of 121.
- v1.0.1 skips every installed copy of this skill found below the scanned folder and lists what it skipped. A copy is recognised as the scanner's own folder, or by
name: stripe-endive-upgrade-checkin its SKILL.md. - v1.0.1 adds
--exclude <glob>and--include-self. - The rules, the fixes and the default skips (
node_modules,.git, virtualenvs, build output) are unchanged.
The new tests fail on v1.0.0. As a check, the v1.0.1 tests/test_scan.py was run against the v1.0.0 scanner: 5 failures and 5 errors, all in the new self-skip and exclude tests.
What is real and what is mocked
- Real: the scanner as shipped, the real stripe-node 22.6.2 / 23.0.1 and stripe-python 16.0.0 packages (installed from npm/PyPI during the run), and the TypeScript compiler checking the fixture app against the SDKs' own type definitions.
- Mocked: Stripe's API. The SDK probes point the real SDKs at a local HTTP server and check what they send (headers, form fields) and what they reject. No request went to Stripe. The HTTP 400
payment_method_types_no_longer_supportedon Endive comes from Stripe's changelog (reference/MIGRATION-NOTES.md); we did not trigger it against a real Stripe account. - Webhook signatures in the probes are made with a throwaway test secret using Stripe's documented
t=…,v1=…scheme.
1. Scanner unit tests: python3 tests/test_scan.py (27/27)
New in v1.0.1:
SelfSkip: builds a repo from the before-app and installs the skill in it: one copy in.cursor/skills/, one copy in.claude/skills/, two copies, or a copy under another folder name. It then runs the installed copy's scanner on the repo root. Every finding must equal the clean before-app baseline (same rule, file, line and severity; 15/4/6), with 0 findings from.claude/or.cursor/. These tests also check:- the negative control:
--include-selfbrings the false findings back; - someone else's skill folder is still scanned;
- an explicit path inside the skill is still scanned.
Exclude:--excludeby folder name, by glob and by relative path, repeated; that it is reported in JSON; and that.gitandnode_modulesare skipped by default.
test_fixed_app_has_no_blockers_or_warnings (__main__.AfterApp.test_fixed_app_has_no_blockers_or_warnings) ... ok
test_strict_passes_on_fixed_app (__main__.AfterApp.test_strict_passes_on_fixed_app) ... ok
test_counts (__main__.BeforeApp.test_counts) ... ok
test_every_rule_is_exercised (__main__.BeforeApp.test_every_rule_is_exercised) ... ok
test_exit_code_1_when_blockers (__main__.BeforeApp.test_exit_code_1_when_blockers) ... ok
test_findings_have_fix_text (__main__.BeforeApp.test_findings_have_fix_text) ... ok
test_finds_every_seeded_issue_and_nothing_else (__main__.BeforeApp.test_finds_every_seeded_issue_and_nothing_else) ... ok
test_engines_20_or_later_not_flagged (__main__.Behaviour.test_engines_20_or_later_not_flagged) ... ok
test_missing_path_exit_2 (__main__.Behaviour.test_missing_path_exit_2) ... ok
test_python_positional_fourth_arg_zero (__main__.Behaviour.test_python_positional_fourth_arg_zero) ... ok
test_single_file_argument (__main__.Behaviour.test_single_file_argument) ... ok
test_skips_node_modules_and_venv (__main__.Behaviour.test_skips_node_modules_and_venv) ... ok
test_strict_fails_on_warnings_only (__main__.Behaviour.test_strict_fails_on_warnings_only) ... ok
test_text_output_summary_line (__main__.Behaviour.test_text_output_summary_line) ... ok
test_exclude_folder_by_name (__main__.Exclude.test_exclude_folder_by_name) ... ok
test_exclude_glob_and_relative_path_repeatable (__main__.Exclude.test_exclude_glob_and_relative_path_repeatable) ... ok
test_exclude_reported_in_json (__main__.Exclude.test_exclude_reported_in_json) ... ok
test_git_and_node_modules_skipped_by_default (__main__.Exclude.test_git_and_node_modules_skipped_by_default) ... ok
test_reads_comments_and_fixed_forms_not_flagged (__main__.FalsePositives.test_reads_comments_and_fixed_forms_not_flagged) ... ok
test_copy_under_another_folder_name_found_by_skill_md (__main__.SelfSkip.test_copy_under_another_folder_name_found_by_skill_md) ... ok
test_explicit_root_inside_the_skill_is_scanned (__main__.SelfSkip.test_explicit_root_inside_the_skill_is_scanned) ... ok
test_negative_control_include_self_brings_the_false_findings_back (__main__.SelfSkip.test_negative_control_include_self_brings_the_false_findings_back) ... ok
test_one_copy_claude (__main__.SelfSkip.test_one_copy_claude) ... ok
test_one_copy_cursor (__main__.SelfSkip.test_one_copy_cursor) ... ok
test_other_skills_are_still_scanned (__main__.SelfSkip.test_other_skills_are_still_scanned) ... ok
test_scanner_outside_repo_still_skips_installed_copy (__main__.SelfSkip.test_scanner_outside_repo_still_skips_installed_copy) ... ok
test_two_copies_either_scanner (__main__.SelfSkip.test_two_copies_either_scanner) ... ok
Ran 27 tests — OK
2. Skill structure checks: python3 tests/test_skill_structure.py (8/8)
These check:
- the frontmatter: name = folder name, lowercase-hyphen, plain ASCII, at most 64 characters;
- the description: length, trigger words, no angle brackets;
- that every file the skill mentions exists;
- that every scanner rule is documented in SKILL.md;
- that none of the banned claim words from design-rules.md (the list is in the test) appears in SKILL.md or README.md;
- new in v1.0.1: that step 1 explains the skipped folders and
--exclude.
test_description (__main__.Skill.test_description) ... ok
test_every_rule_documented (__main__.Skill.test_every_rule_documented) ... ok
test_has_frontmatter (__main__.Skill.test_has_frontmatter) ... ok
test_name (__main__.Skill.test_name) ... ok
test_name_and_folder_plain_ascii (__main__.Skill.test_name_and_folder_plain_ascii) ... ok
test_no_banned_words (__main__.Skill.test_no_banned_words) ... ok
test_referenced_files_exist (__main__.Skill.test_referenced_files_exist) ... ok
test_scan_step_explains_skips_and_exclude (__main__.Skill.test_scan_step_explains_skips_and_exclude) ... ok
Ran 8 tests — OK
3. Example task: scan a real-looking app before and after the fixes
$ python3 scripts/endive_scan.py tests/fixtures/before-app
endive_scan 1.0.1: 15 blocker(s), 4 warning(s), 6 info
before-app exit code 1 (expect 1), after-app --strict exit code 0 (expect 0)
The before-app has TypeScript, plain JavaScript, React and Python files; the scanner's golden test (section 1) checks that it reports exactly the seeded findings and nothing in false-positives/.
4. TypeScript against the real stripe-node types and runtime probes (stripe-node 22.6.2 / 23.0.1)
--- tsc -p tsconfig.before.v22.json (expect exit 0)
exit 0
--- tsc -p tsconfig.after.v23.json (expect exit 0)
exit 0
--- tsc -p tsconfig.before.v23.json (expect exit 2)
../../fixtures/before-app/src/checkout.ts(8,5): error TS2561: Object literal may only specify known properties, but 'payment_method_types' does not exist in type 'SessionCreateParams'. Did you mean to write 'payment_method_data'?
../../fixtures/before-app/src/checkout.ts(15,66): error TS2561: Object literal may only specify known properties, but 'payment_method_types' does not exist in type 'PaymentIntentCreateParams'. Did you mean to write 'payment_method'?
../../fixtures/before-app/src/checkout.ts(19,47): error TS2322: Type 'string' is not assignable to type 'BillingCycleAnchor'.
../../fixtures/before-app/src/webhook.ts(23,17): error TS2339: Property 'constructEventWithoutVerification' does not exist on type 'Stripe'.
exit 2
--- the before-app on stripe-node 23 must fail with exactly these errors:
1 error TS2322
1 error TS2339
2 error TS2561
PASS error set matches
PASS stripe-node 22.6.2 sends Stripe-Version 2026-08-26.dahlia by default
PASS stripe-node 23.0.1 sends Stripe-Version 2026-09-30.endive by default
PASS stripe-node 23.0.1 with apiVersion pinned sends the pinned version (upgrade SDK and API separately)
PASS stripe-node 23.0.1 still SENDS payment_method_types from plain JS (Stripe then answers 400 on Endive)
PASS fixed Checkout create sends allowed_payment_method_types[0]=card
PASS fixed Checkout create sends no bare payment_method_types
PASS fixed PaymentIntent create sends allowed_payment_method_types[0]=card to /v1/payment_intents
PASS fixed billing_cycle_anchor is sent as billing_cycle_anchor[type]=now
PASS 22.6.2: fresh signature accepted by constructEvent
PASS 22.6.2: 10-minute-old signature rejected by constructEvent with default tolerance
PASS 22.6.2: 10-minute-old signature with constructEvent(..., 0) -> rejected
PASS 22.6.2: 10-minute-old signature with signature.verifyHeader(3 args) -> accepted
PASS 22.6.2: wrong secret always rejected
PASS 23.0.1: fresh signature accepted by constructEvent
PASS 23.0.1: 10-minute-old signature rejected by constructEvent with default tolerance
PASS 23.0.1: 10-minute-old signature with constructEvent(..., 0) -> accepted
PASS 23.0.1: 10-minute-old signature with signature.verifyHeader(3 args) -> rejected
PASS 23.0.1: wrong secret always rejected
PASS 23.0.1: signature.verifyHeader(..., 0) skips the timestamp check (documented opt-out)
PASS 22.6.2: top-level stripe.constructEventWithoutVerification exists
PASS 23.0.1: top-level stripe.constructEventWithoutVerification is gone
PASS 23.0.1: stripe.webhooks.constructEventWithoutVerification works
PASS stripe-node 23.0.1 package.json declares engines.node >=20
23/23 SDK probe checks passed
5. stripe-python 16.0.0 probes (fresh venv)
PASS stripe-python 16.0.0 pins api_version 2026-09-30.endive
PASS StripeClient sends Stripe-Version 2026-09-30.endive by default
PASS fixed Checkout create sends allowed_payment_method_types[0]=card
PASS stripe-python 16 still SENDS payment_method_types if you pass it (Stripe then answers 400 on Endive)
PASS fixed billing_cycle_anchor is sent as billing_cycle_anchor[type]=now
PASS SP003: client.v1.customers.retrieve(customer=...) raises TypeError in 16.0.0
PASS SP003 fix: positional id works and calls /v1/customers/cus_123
PASS SP002: stripe.Reversal is gone in 16.0.0
PASS SP002 fix: stripe.TransferReversal exists
PASS SP004: StripeObject.request() is gone in 16.0.0
PASS SP004 fix: StripeClient.raw_request exists
PASS fresh signature accepted
PASS 10-minute-old signature rejected with default tolerance (300 s)
PASS SP001: 10-minute-old signature ACCEPTED with tolerance=0
PASS wrong secret rejected
15/15 SDK probe checks passed
Not tested
- Real AI agents using the skill in Claude Code or Cursor. No live session ran (see "Not run inside Claude Code or Cursor" above). The skill's structure and its scan command are checked; no agent followed its instructions.
- Any request to real Stripe (test or live mode), Stripe Workbench, the API-version upgrade and its 72-hour rollback, and Stripe.js / Elements in a browser (rules SE002, SE003 come from Stripe's changelog and are checked by pattern only).
- Rule SN001 (
Stripe.ErrorTyperemoved): taken from Stripe's v23 migration guide. We could not reproduce it by compiling, because the type isn't reachable from the defaultstripeimport in 22.6.2 either; the scanner flags the pattern only. - SP004 (
StripeObject.request()removed) has no scanner rule (it can't be told apart from other.request()calls); the Python probe confirms the API is gone, and SKILL.md covers it as a manual check. - Ruby, PHP, Go, Java and .NET code, monorepos with unusual layouts, and parameters built at run time.
- Windows and macOS paths. The self-skip was tested on Linux only, though it uses
os.paththroughout.
Known limits (see SKILL.md, "Limits")
- Pattern scanning can miss code that builds Stripe parameters dynamically and can flag look-alikes; the false-positive fixtures cover the common cases we found.
- Folders skipped by default or with
--excludeare not checked. Pass such a folder's path explicitly to scan it.