From 6e14522a84b87fbceaccaa5a39899bab3cbbb71b Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Fri, 16 Oct 2026 17:24:12 +0200 Subject: [PATCH] docs(quick-261016-nqk): plan and summary for UAT gaps G-18.1-7 and G-18.1-4; D-21 amended, COVERAGE cancel_at row --- .../18.1-CONTEXT.md | 1 + .../COVERAGE.md | 2 +- .../261016-nqk-PLAN.md | 21 +++++++++++ .../261016-nqk-SUMMARY.md | 37 +++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 .planning/quick/261016-nqk-close-uat-gaps-g-18-1-7-and-g-18-1-4/261016-nqk-PLAN.md create mode 100644 .planning/quick/261016-nqk-close-uat-gaps-g-18-1-7-and-g-18-1-4/261016-nqk-SUMMARY.md diff --git a/.planning/phases/18.1-payment-gateway-subscriptions/18.1-CONTEXT.md b/.planning/phases/18.1-payment-gateway-subscriptions/18.1-CONTEXT.md index c29f64f..66ccba3 100644 --- a/.planning/phases/18.1-payment-gateway-subscriptions/18.1-CONTEXT.md +++ b/.planning/phases/18.1-payment-gateway-subscriptions/18.1-CONTEXT.md @@ -66,6 +66,7 @@ Research (`18.1-RESEARCH.md` § Premise Shifts S-1..S-10) found premises that no - **D-20 (S-8, S-9):** The Stripe subscriptions subpackage is its own party plugin, `golem15.pgstripesubscriptions`, at module path `git.golem15.com/golem15/sm-pgstripe-plugin/subscriptions`. It gets its own importable or package-local Stripe fake; P18's root `stripefake_test.go` stays unchanged. - **D-21:** Behaviour defaults: keep PHP's `cancel_at_period_end` mapping (a manual test-mode check decides whether `cancel_at` from the billing portal also needs handling); out-of-order events behave as PHP does; the job reads both pre-basil and basil payload shapes; customer `GET /subscription` returns the latest subscription with `has_access` and `in_grace_period` flags. - **D-21 amendment (2026-10-14, gap closure 18.1-07, user decision on verification human item 1):** out-of-order events no longer behave as PHP; created and updated events apply the subscription Stripe holds, serialised per subscription, and a deletion before its subscription is stored is retried. D-21's other defaults are unchanged. +- **D-21 amendment (2026-10-16, quick 261016-nqk, UAT test 7 / gap G-18.1-7):** the test-mode check settled the `cancel_at` question. On API `2026-09-30.endive` a billing-portal cancel reports `cancel_at` (the period end) with `cancel_at_period_end` false; an API cancel reports both. pgstripe now stores `cancel_at_period_end` true when Stripe reports that flag or a `cancel_at` date on an active or trialing subscription; clearing `cancel_at` maps back to false and fires `reactivated`. The row keeps PHP's single flag; `cancel_at` itself is not stored. - **D-22 (plan split, 6 plans):** (1) framework: cabana flat reorder + `taglist`; (2) pgsub core tracer in the new repo (created with `tea repos create --owner golem15`); (3) pgstripe secret columns + import mapping + `subscriptions` plugin (gateway, both webhook routes, River job, host test); (4) pgsub admin screens + customer API; (5) `vue-paymentgateway-layer` pieces (tag `v0.2.0` only on go-ahead); (6) unit tests across all repos + `scripts/check-phase18.1.sh`. Plan 2's repo is committed before plan 3's `go.mod` points at it. No push, tag or deploy without the user's go-ahead. ### Claude's Discretion diff --git a/.planning/phases/18.1-payment-gateway-subscriptions/COVERAGE.md b/.planning/phases/18.1-payment-gateway-subscriptions/COVERAGE.md index 8f70a53..68d4b86 100644 --- a/.planning/phases/18.1-payment-gateway-subscriptions/COVERAGE.md +++ b/.planning/phases/18.1-payment-gateway-subscriptions/COVERAGE.md @@ -17,7 +17,7 @@ Scope: the Stripe Billing surface the frozen PHP `SubscriptionGateway` contract | subscriptions.retrieve (syncSubscription / reconcile) | INTEGRATE | also the webhook job's fresh-state read: created/updated apply Stripe's current subscription, 404 falls back to the snapshot (CR-01, 18.1-07); used by Service.SyncSubscription (WR-06) | | subscriptions.update cancel_at_period_end true (cancel at period end) | INTEGRATE | | | subscriptions.cancel (cancel immediately) | INTEGRATE | | -| subscriptions.update cancel_at (flexible-billing cancel enums) | OPT-OUT | D-21 keeps PHP's cancel_at_period_end mapping; a manual Stripe test-mode check at UAT decides a follow-up | +| subscription cancel_at (read from events and retrieval) | INTEGRATE | a cancel_at date on an active or trialing subscription maps to cancel_at_period_end true; cleared maps back (portal cancels report cancel_at only; D-21 amended 2026-10-16) | | subscriptions.update (resume or un-cancel through the API) | OPT-OUT | not in the frozen PHP contract: reactivation happens in the billing portal and arrives as customer.subscription.updated | | subscriptions.update (plan change, proration) | OPT-OUT | not in the frozen PHP contract: plan changes go through the billing portal | | subscriptions.list / subscriptions.search | OPT-OUT | not in the frozen PHP contract: reconcile walks local rows and retrieves each | diff --git a/.planning/quick/261016-nqk-close-uat-gaps-g-18-1-7-and-g-18-1-4/261016-nqk-PLAN.md b/.planning/quick/261016-nqk-close-uat-gaps-g-18-1-7-and-g-18-1-4/261016-nqk-PLAN.md new file mode 100644 index 0000000..633b169 --- /dev/null +++ b/.planning/quick/261016-nqk-close-uat-gaps-g-18-1-7-and-g-18-1-4/261016-nqk-PLAN.md @@ -0,0 +1,21 @@ +--- +quick_id: 261016-nqk +slug: close-uat-gaps-g-18-1-7-and-g-18-1-4 +date: 2026-10-16 +gap_closure: true +gap_ids: [G-18.1-7, G-18.1-4] +repos: [summercms.go, sm-paymentgatewaysubscriptions-plugin, sm-pgstripe-plugin] +source: .planning/phases/18.1-payment-gateway-subscriptions/18.1-UAT.md (## Gaps) +--- + +# Quick 261016-nqk: close UAT gaps G-18.1-7 and G-18.1-4 + +No push, tag or release. Framework first (pgsub needs its new hooks), then the plugins; planning docs last. + +## Tasks + +1. **G-18.1-7 (pgstripe).** The decoded subscription maps `cancel_at_period_end` true for Stripe's flag or a `cancel_at` date while active or trialing; a cleared `cancel_at` maps back. One choke point (`subscriptionObject.args`) serves the snapshot, the fresh retrieval and `SyncSubscription`. Tests: both payload shapes, un-cancel, `cancel_at` in past_due/canceled; Postgres smoke of portal cancel then un-cancel (reactivated once). README, COVERAGE row, D-21 amendment. +2. **G-18.1-4 (preview labels, pgsub).** The label element renders its `value` property (the SPA's plugin-element contract), not only the attribute. Node-run test. +3. **G-18.1-4 (framework).** `pact.ListExtendRecords` (once per list page, before columns are read; unstored `gorm:"-"` columns compile unsortable and refuse sortable/searchable) and `pact.FormRecordTitle` (`meta.title` on show/create/update; the SPA heads form and preview with it). Roster fixture tests, SPA tests, docs, OpenAPI, admin dist. +4. **G-18.1-4 (pgsub).** Subscriber column (e-mail, else name; ` #` fallback) through `ListExtendRecords` and the optional `contracts.SubscriberBatchResolver` (user resolver implements `ResolveMany`); subscriber type and id become invisible searchable columns; preview title "Subscription # ()" (en/pl). Tests, README. +5. Rebuild the UAT demo app and check the admin API. diff --git a/.planning/quick/261016-nqk-close-uat-gaps-g-18-1-7-and-g-18-1-4/261016-nqk-SUMMARY.md b/.planning/quick/261016-nqk-close-uat-gaps-g-18-1-7-and-g-18-1-4/261016-nqk-SUMMARY.md new file mode 100644 index 0000000..002b2b9 --- /dev/null +++ b/.planning/quick/261016-nqk-close-uat-gaps-g-18-1-7-and-g-18-1-4/261016-nqk-SUMMARY.md @@ -0,0 +1,37 @@ +--- +quick_id: 261016-nqk +slug: close-uat-gaps-g-18-1-7-and-g-18-1-4 +status: complete +date: 2026-10-16 +gap_closure: true +gap_ids: [G-18.1-7, G-18.1-4] +repos: + sm-pgstripe-plugin: [a5b3314] + sm-paymentgatewaysubscriptions-plugin: [6a3f2c4, d5579cd] + summercms.go: [bc5685e, 013890c, 17398fe] +--- + +# Quick 261016-nqk: UAT gaps G-18.1-7 and G-18.1-4 + +Nothing pushed, tagged or released. + +| Gap | Fix | Commit | +|-----|-----|--------| +| G-18.1-7 | `cancel_at_period_end` = Stripe's flag, or a `cancel_at` date on an active or trialing subscription; un-cancel maps back and fires reactivated. D-21 amended; COVERAGE row now INTEGRATE | pgstripe a5b3314 | +| G-18.1-4 (empty Gateway/Status on preview) | the label element renders its `value` property (the SPA sets the property, the element read only the attribute) | pgsub 6a3f2c4 | +| G-18.1-4 (framework) | `pact.ListExtendRecords`, `pact.FormRecordTitle` (`meta.title`), unstored list columns unsortable/unsearchable; SPA uses `meta.title`; docs, OpenAPI, dist | summercms.go bc5685e, 013890c, 17398fe | +| G-18.1-4 (subscriber, title) | list column `subscriber` (e-mail, else name, ` #` fallback) via the new optional `contracts.SubscriberBatchResolver`; type/id invisible but searchable; preview title "Subscription # ()" / "Subskrypcja # ()" | pgsub d5579cd | + +The screenshot's "Cancel at Period End: No / Canceled At: —" was stale: the preview API for subscription 1 returns `cancel_at_period_end: true` and `canceled_at` (checked on the demo, before and after the rebuild). + +## Verification + +- summercms.go: `go vet ./...`, `go test ./...` (Docker), `summer docs:build --check`, `TestDocsTree`, admin vitest 1431/1431 and typecheck, `check-admin-openapi.sh`, `check-admin-dist.sh`. +- pgsub and pgstripe: `go vet ./...`, `go test ./...` (Docker) green; the new subtests ran (not skipped). +- Demo app rebuilt without overlays and restarted: list row `subscriber: customer@example.test`, preview `meta.title: "Subscription #1 (Pro)"`, `gateway: Stripe`, `stripe_status: Active`; the new admin bundle is served. + +## Release notes + +- pgsub now needs the framework's `pact.ListExtendRecords` and `pact.FormRecordTitle`: tag the framework (v0.2.2) first, bump pgsub's `require`, tag pgsub, then pgstripe (already needs unreleased pgsub from 261016-myi). +- Search by subscriber e-mail is not offered: cabana searches stored columns and relations only, and the subscriber is polymorphic. Type and id stay searchable. +- UAT 7 and the site-side cancel still need a live re-run in the demo.