docs(18.1-09): complete gap-closure tests, phase gate and release plan
This commit is contained in:
@@ -0,0 +1,316 @@
|
||||
---
|
||||
phase: 18.1-payment-gateway-subscriptions
|
||||
plan: 09
|
||||
subsystem: payments
|
||||
tags: [stripe, subscriptions, testing, postgres, testcontainers, phase-gate, bfcache, release, gap-closure]
|
||||
status: complete
|
||||
|
||||
requires:
|
||||
- phase: 18.1-payment-gateway-subscriptions
|
||||
provides: "plan 07 (CR-01 fresh-state webhook job, WithSessionLock, SubscriptionRetriever, plan resolution, WR-06 sync), plan 08 (CR-02 strict price, WR-02/WR-03 idempotency, guard and lock, WR-01 Detached bulk action, WR-07 page restore), plan 06 (scripts/check-phase18.1.sh, release order)"
|
||||
provides:
|
||||
- "Named tests for every gap-closure fix of plans 07 and 08 in four repositories (framework cabana, pgsub, pgstripe, layer)"
|
||||
- "scripts/check-phase18.1.sh requires the new names per stage and package; layers minimum raised to 43"
|
||||
- "18.1-VALIDATION.md rows for every task of plans 07 to 09 (validated, nyquist_compliant)"
|
||||
- "Layer package.json 0.2.1"
|
||||
- "Release per the reply push-all-with-plugin-tags: framework v0.2.1, pgsub v0.1.0, pgstripe v0.1.0, layer v0.2.1"
|
||||
affects: [18.1-verify-work, pgsub-consumers, pgstripe-consumers, layer-consumers, cabana-consumers]
|
||||
|
||||
actuals:
|
||||
tokens: 25537 # chars/4 over the added lines of the realized diff (4 repos, tasks 1-4)
|
||||
tasks: 5
|
||||
commits: 12 # MEASURED per repo before this SUMMARY commit: summercms.go 7 + pgsub 1 + pgstripe 2 + layer 2
|
||||
commits: 12
|
||||
plan_head_before: "summercms.go f82ac0f2f5c322db3913f022d83fc45088b29a77; pgsub b6738d3f0246e586268caeb219e53ff6991790b2; pgstripe c9bd95308531320ea879a41e8e00da6a7b670d7b; layer 34cf926c4558446cd96c74fd94306b7727eb50ec"
|
||||
plan_head_after: "summercms.go 03b8cc2; pgsub 2e21084; pgstripe cc80ea7; layer 22368c5 (before this SUMMARY commit and the release commits)"
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Stripe fake failure switches (setFailRetrieve) and recorded request headers for idempotency assertions"
|
||||
- "Open-transaction probe in a fake gateway hook (pg_stat_activity idle in transaction) to prove a provider call runs outside a transaction"
|
||||
- "Row-lock probe from a second connection (SELECT ... FOR UPDATE NOWAIT) to prove a detached bulk action holds no lock"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- modules/cabana/bulk_detached_test.go
|
||||
- ../sm-pgstripe-plugin/subscriptions/ordering_test.go
|
||||
- ../sm-pgstripe-plugin/subscriptions/gateway_hardening_test.go
|
||||
- ../sm-paymentgatewaysubscriptions-plugin/models/checkout_price_test.go
|
||||
- ../sm-paymentgatewaysubscriptions-plugin/plan_resolution_test.go
|
||||
- ../sm-paymentgatewaysubscriptions-plugin/sync_subscription_test.go
|
||||
- ../sm-paymentgatewaysubscriptions-plugin/checkout_guard_test.go
|
||||
- ../sm-paymentgatewaysubscriptions-plugin/admin_sync_detached_test.go
|
||||
modified:
|
||||
- scripts/check-phase18.1.sh
|
||||
- scripts/check-phase10.sh
|
||||
- modules/cabana/README.md
|
||||
- ../sm-pgstripe-plugin/subscriptions/stripefake_test.go
|
||||
- ../sm-pgstripe-plugin/subscriptions/decode_test.go
|
||||
- ../sm-pgstripe-plugin/subscriptions/README.md
|
||||
- ../sm-paymentgatewaysubscriptions-plugin/fixture_plugin_test.go
|
||||
- ../sm-paymentgatewaysubscriptions-plugin/README.md
|
||||
- ../vue-paymentgateway-layer/tests/subscribe.test.ts
|
||||
- ../vue-paymentgateway-layer/tests/subscriptionComponents.test.ts
|
||||
- ../vue-paymentgateway-layer/package.json
|
||||
- ../vue-paymentgateway-layer/README.md
|
||||
- .planning/phases/18.1-payment-gateway-subscriptions/18.1-VALIDATION.md
|
||||
- .planning/phases/18.1-payment-gateway-subscriptions/deferred-items.md
|
||||
|
||||
key-decisions:
|
||||
- "The framework release tag is v0.2.1, not the v0.1.27 the plan proposed: v0.1.27 and v0.2.0 already exist on origin (other sessions' releases), and tags are immovable"
|
||||
- "First plugin tags v0.1.0 on pgsub and pgstripe; pgstripe requires pgsub v0.1.0; replace lines stay (sm-paymentgateway-plugin is untagged, IN-08)"
|
||||
- "TestDetachedBulkAction is named outside ^TestBulkAction so plan 08's count of 13 stays meaningful"
|
||||
|
||||
patterns-established:
|
||||
- "A gap closure's last plan names one test per review finding and the phase gate requires each by stage and package"
|
||||
|
||||
requirements-completed: [SC-5, D-22, CR-01, CR-02, WR-01, WR-02, WR-03, WR-04, WR-06, WR-07]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "CR-01: the three review sequences (checkout race in either order and concurrent, deleted before created, stale cancel flag) plus retrieval failure retried and no Stripe call for duplicates or other apps"
|
||||
requirement: CR-01
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "sm-pgstripe-plugin/subscriptions/ordering_test.go#TestEventOrdering"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "sm-paymentgatewaysubscriptions-plugin/plan_resolution_test.go#TestApplyDeletedNotStored"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "CR-02: strict per-currency checkout price (models, gateway, API 422 before any gateway call)"
|
||||
requirement: CR-02
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "sm-paymentgatewaysubscriptions-plugin/models/checkout_price_test.go#TestCheckoutPriceID"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "sm-pgstripe-plugin/subscriptions/gateway_hardening_test.go#TestGatewayStrictPrice"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "sm-paymentgatewaysubscriptions-plugin/checkout_guard_test.go#TestCustomerAPIGuards"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "WR-02/WR-03: first-write-wins customer id, guarded checkout (open statuses refused, concurrent checkouts of one subscriber never overlap at the gateway), idempotency keys"
|
||||
requirement: WR-03
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "sm-paymentgatewaysubscriptions-plugin/checkout_guard_test.go#TestCheckoutGuarded"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "sm-paymentgatewaysubscriptions-plugin/checkout_guard_test.go#TestCustomerIDFirstWriteWins"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "sm-pgstripe-plugin/subscriptions/gateway_hardening_test.go#TestGatewayIdempotencyKeys"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "sm-paymentgatewaysubscriptions-plugin/models/checkout_price_test.go#TestTerminalStatuses"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "WR-01: detached admin bulk actions (framework) and the plan sync actions run without a transaction or row lock across gateway calls"
|
||||
requirement: WR-01
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "summercms.go/modules/cabana/bulk_detached_test.go#TestDetachedBulkAction"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "sm-paymentgatewaysubscriptions-plugin/admin_sync_detached_test.go#TestAdminSyncDetached"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D5
|
||||
description: "WR-04/WR-06: plan resolution by price, stripe_prices, product and plan_code; fresh sync writes gateway-owned columns only under the lock"
|
||||
requirement: WR-06
|
||||
verification:
|
||||
- kind: integration
|
||||
ref: "sm-paymentgatewaysubscriptions-plugin/plan_resolution_test.go#TestPlanResolution"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "sm-paymentgatewaysubscriptions-plugin/sync_subscription_test.go#TestSyncSubscriptionFresh"
|
||||
status: pass
|
||||
- kind: integration
|
||||
ref: "sm-pgstripe-plugin/subscriptions/gateway_hardening_test.go#TestRetrieveSubscription"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "sm-pgstripe-plugin/subscriptions/decode_test.go#TestDecodeSubscription"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D6
|
||||
description: "WR-07: back/forward-cache restore of the subscribe and manage flows, guarded navigation, component wiring, the already-subscribed copy"
|
||||
requirement: WR-07
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "vue-paymentgateway-layer/tests/subscribe.test.ts#subscriptions: a persisted pageshow restores the subscribe flow after a redirect"
|
||||
status: pass
|
||||
- kind: unit
|
||||
ref: "vue-paymentgateway-layer/tests/subscriptionComponents.test.ts#subscriptions: the three components wire watchPageRestore on mount and remove it on unmount"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D7
|
||||
description: "Live Stripe test-mode checkout (created and updated in either order), portal cancel_at, and the browser pass including Back from Checkout"
|
||||
verification: []
|
||||
human_judgment: true
|
||||
rationale: "Manual-only rows kept in 18.1-VALIDATION.md; they need a real Stripe test account and a browser"
|
||||
|
||||
duration: "plan start 2026-10-16T10:55:35Z (tasks 1-4 by the first executor, Task 5 by a continuation executor)"
|
||||
completed: 2026-10-16
|
||||
---
|
||||
|
||||
# Phase 18.1 Plan 09: Gap-closure tests, phase gate and release Summary
|
||||
|
||||
**Named Postgres, Docker-free and TAP tests pin every fix of plans 07 and 08 (CR-01 ordering, CR-02 strict price, WR-01 detached bulk actions, WR-02/03 guard and idempotency, WR-04/06 plan resolution and fresh sync, WR-07 page restore). The phase gate requires each test by name, and the gap closure was released as framework v0.2.1, pgsub v0.1.0, pgstripe v0.1.0 and layer v0.2.1.**
|
||||
|
||||
## Commits per repository (tasks 1 to 4)
|
||||
|
||||
| Task | summercms.go | pgsub | pgstripe | layer |
|
||||
|------|--------------|-------|----------|-------|
|
||||
| 1 CR-01 ordering suite (tracer) | `0fc909d` gate | | `768447b` | |
|
||||
| 2 pgsub coverage | `5a446d6` gate | `2e21084` | | |
|
||||
| 3 pgstripe and framework coverage | `fa91e2a` cabana test, `b6e6736` gate | | `cc80ea7` | |
|
||||
| 4 layer coverage, 0.2.1, gate, VALIDATION | `3cb2252` gate, `48e2642` check-phase10 fix, `03b8cc2` VALIDATION and deferred items | | | `c535975` specs, `22368c5` 0.2.1 |
|
||||
|
||||
The tracer gate after Task 1 re-ran the postgres stage end to end and it passed. A mutation check (applying the event snapshot again) turned 5 of the 7 `TestEventOrdering` subtests red.
|
||||
|
||||
## Tests added
|
||||
|
||||
**Framework (cabana)**
|
||||
- `TestDetachedBulkAction` (Postgres):
|
||||
- a detached Run sees no transaction (`TxFromContext` false);
|
||||
- another connection's `SELECT ... FOR UPDATE NOWAIT` on a selected row succeeds during Run;
|
||||
- the empty, absent and partial-selection rules (affected 0 without Run, 409);
|
||||
- a `ValidationError` answers 422, and a row written before it stays written;
|
||||
- the same action without `Detached` still holds the transaction and the lock.
|
||||
|
||||
**pgsub**
|
||||
- Docker-free: `TestCheckoutPriceID` (CR-02), `TestTerminalStatuses` (WR-03).
|
||||
- Postgres:
|
||||
- `TestPlanResolution` (WR-04): price, stripe_prices, product, plan_code (inactive plan included), the unresolved error, an existing row keeps its plan;
|
||||
- `TestApplyDeletedNotStored` (CR-01): the sentinel, nil row, no event;
|
||||
- `TestSyncSubscriptionFresh` (WR-06): retriever and legacy paths, gateway-owned columns only, no events, waits for a held `SubscriptionLockKey`;
|
||||
- `TestCheckoutGuarded` (WR-03): the six open statuses refused without a gateway call, canceled and incomplete_expired allowed, concurrent checkouts of one subscriber never overlap, two subscribers in parallel, one customer;
|
||||
- `TestCustomerIDFirstWriteWins` (WR-02);
|
||||
- `TestCustomerAPIGuards` (WR-03, CR-02): 409 for past_due, unpaid, incomplete and paused with the check order unchanged, 422 for a currency without a price;
|
||||
- `TestAdminSyncDetached` (WR-01): no gateway call while the session is idle in transaction, a failed save is a failure line, ids saved after the request context was cancelled.
|
||||
|
||||
**pgstripe**
|
||||
- `TestEventOrdering` (Postgres, CR-01). Subtests:
|
||||
- `checkout_race_updated_first`
|
||||
- `checkout_race_concurrent`
|
||||
- `deleted_before_created`
|
||||
- `stale_cancel_flag_no_reactivated`
|
||||
- `retrieve_error_retries`
|
||||
- `duplicate_makes_no_stripe_call`
|
||||
- `other_app_makes_no_stripe_call`
|
||||
- Gateway tests (Postgres, for the settings):
|
||||
- `TestGatewayStrictPrice` (CR-02);
|
||||
- `TestGatewayIdempotencyKeys` (WR-02, WR-03);
|
||||
- `TestRetrieveSubscription` (CR-01, WR-06).
|
||||
- `TestDecodeSubscription` gained the price's product (string and expanded) and `plan_code`. Job arguments still hold no personal data.
|
||||
- Fake: `setFailRetrieve(bool)`, and `fakeRequest` now keeps the `Idempotency-Key` header.
|
||||
|
||||
**Layer**
|
||||
- Seven new `subscriptions:` specs, which raises the count from 36 to 43:
|
||||
- a persisted pageshow restores the subscribe flow;
|
||||
- a non-persisted pageshow changes nothing;
|
||||
- `watchPageRestore`'s remover and its server no-op;
|
||||
- a throwing navigation re-enables the button;
|
||||
- the manage flow restores;
|
||||
- the components' mount and unmount wiring;
|
||||
- the already-subscribed copy in en and pl.
|
||||
- `package.json` is 0.2.1. `pnpm-lock.yaml` is unchanged.
|
||||
|
||||
## Gate result
|
||||
|
||||
The Task 5 sheet run of `PHASE181_REQUIRE_POSTGRES=1 bash scripts/check-phase18.1.sh --all` (clean trees, then `--all`) printed `final-push-sheet-ok`:
|
||||
|
||||
| Stage | Result |
|
||||
|-------|--------|
|
||||
| self-test | PASS |
|
||||
| go | 5 required framework tests |
|
||||
| plugins | pgsub models 5, contracts 1, root 5; pgstripe root 4, subscriptions 3; vet and `-short` green |
|
||||
| admin | vue-tsc; vitest 1429 tests in 93 files |
|
||||
| layers | 112 TAP tests, 0 failures; 43 named `subscriptions:` (minimum 43) |
|
||||
| docs | TestDocsTree, docs:build, OpenAPI check, dist check, phase 10 hygiene |
|
||||
| postgres | framework 4; pgsub 21; pgstripe root 4, subscriptions 12 |
|
||||
|
||||
No suite was NOT RUN.
|
||||
|
||||
## VALIDATION
|
||||
|
||||
`18.1-VALIDATION.md` stays `status: validated`, `nyquist_compliant: true` and `wave_0_complete: true`, revalidated 2026-10-16. It has one row per task of plans 07, 08 and 09. The manual-only rows remain:
|
||||
- a live test-mode checkout, now also watching created and updated in either order;
|
||||
- the portal `cancel_at` check;
|
||||
- the browser pass, now including Back from Checkout.
|
||||
|
||||
## Task 5: release decision
|
||||
|
||||
**Reply (verbatim):** "push-all-with-plugin-tags, v0.2.1"
|
||||
|
||||
The plan proposed framework tag `v0.1.27`. Since the plan was written, `v0.1.27` and `v0.2.0` were created on origin by other sessions, so the framework tag is `v0.2.1`. Tags are immovable, so the existing tags are left untouched. The plugin requires are raised to `v0.2.1` accordingly.
|
||||
|
||||
**Push sheet.** Every repository was 0 commits behind its origin after `git fetch origin --tags`. Unpushed commits:
|
||||
- summercms.go master: 27 ahead before this SUMMARY.
|
||||
- This phase's commits: plans 07 to 09.
|
||||
- Other sessions' committed work, approved for this push: quick-261016-0fm (admin theme and locale preferences, the dist rebuild for v0.2.1), quick-261015-w7f (docs since v0.1.14, deployed on v0.2.0), the fast task "summercms.io Alpha 0.2 copy", and `9921768` docs(19.3-05).
|
||||
- pgsub main: 7 ahead (plans 07 to 09).
|
||||
- pgstripe main: 8 ahead (plans 07 to 09).
|
||||
- layer main: 3 ahead (plans 08 and 09).
|
||||
|
||||
**Order** (plan 06's order, with plugin tags):
|
||||
1. framework master, then annotated `v0.2.1` on this SUMMARY's commit;
|
||||
2. pgsub: require bump to summercms `v0.2.1` (its own commit), main, annotated `v0.1.0`;
|
||||
3. pgstripe: require bump to summercms `v0.2.1` and pgsub `v0.1.0` (its own commit), main, annotated `v0.1.0`;
|
||||
4. layer: main, annotated `v0.2.1` on `22368c5`.
|
||||
|
||||
Replace lines stay in both plugins. Nothing is force-pushed. The pushed hashes and tag objects are recorded in the release record below, which is added after the pushes.
|
||||
|
||||
**Open item:** the layer's `scripts/check-git-install.sh` still targets `vue-journal-layer`, so an install check of layer `v0.2.1` needs that script fixed first. See `deferred-items.md`.
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- Release per the reply `push-all-with-plugin-tags, v0.2.1`. The framework tag is `v0.2.1` instead of `v0.1.27`, because `v0.1.27` and `v0.2.0` already exist on origin.
|
||||
- New test files per finding group, so gate names map one to one to review findings.
|
||||
- The layer component specs render on the server, so the mount and unmount wiring is asserted from the component sources.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] The phase 10 hygiene allow-list missed the admin theme and locale cache**
|
||||
- **Found during:** Task 4 (`check-phase18.1.sh docs` failed).
|
||||
- **Issue:** `usePreferences.ts` from quick task 261016-0fm caches the theme and locale codes in localStorage. That is an accepted threat of that task (T-261016-0fm-08), but the `check-phase10.sh` hygiene allow-list was not updated, so the docs stage failed. Tokens stay forbidden.
|
||||
- **Fix:** the allow-list now accepts the two preference keys.
|
||||
- **Committed in:** `48e2642`.
|
||||
|
||||
### Adaptations
|
||||
|
||||
1. **The fixture `fakeGateway` gained an `onSync` hook** (pgsub `fixture_plugin_test.go`). `TestAdminSyncDetached` uses it as the open-transaction probe at the moment of the gateway call.
|
||||
2. **`TestDetachedBulkAction` uses test-only actions on the existing roster fixture**, through its wrap hook. No production fixture changed, and the test names no application or provider.
|
||||
3. **pnpm store-dir mismatch after the 0.2.1 bump.** The layer's `pnpm test` aborted without a TTY (`ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY`). `pnpm install --frozen-lockfile --offline --store-dir /media/nvme/dev/golem15/summercms.io/summercms/.pnpm-store` fixed it with the lockfile sha256 unchanged and no package added. It is recorded in `deferred-items.md`.
|
||||
4. **Pre-existing gofmt drift in `modules/cabana` token files.** These are committed files from another session and are not touched here. They are logged in `deferred-items.md`; vet and the tests pass.
|
||||
5. **Framework tag `v0.2.1` instead of `v0.1.27`**, per the human's reply (see Task 5).
|
||||
6. **Most coverage tasks had no failing test first.** They test behaviour plans 07 and 08 already shipped, and no production bug surfaced, so no `fix(18.1-09)` commit exists. The plan is `type: execute`, so the plan-level TDD gate does not apply.
|
||||
|
||||
**Total deviations:** 1 auto-fixed (Rule 3) and 6 adaptations.
|
||||
**Impact on plan:** test-side plumbing and one gate script allow-list. No scope or dependency was added.
|
||||
|
||||
## Deferred items
|
||||
|
||||
| Item | Status |
|
||||
|------|--------|
|
||||
| Layer `scripts/check-git-install.sh` targets `vue-journal-layer` | open (blocks an install check of layer v0.2.1) |
|
||||
| Review findings outside the gap closure (WR-05, IN-01 to IN-08) | open |
|
||||
| gofmt drift in `modules/cabana` token files | open (another session's files) |
|
||||
| pnpm store-dir mismatch in the layer | resolved for the gate; seen again and fixed locally |
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None. This plan adds tests, gate entries and a version string only.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- All created test files exist in their repositories (`bulk_detached_test.go`, `ordering_test.go`, `gateway_hardening_test.go`, `checkout_price_test.go`, `plan_resolution_test.go`, `sync_subscription_test.go`, `checkout_guard_test.go`, `admin_sync_detached_test.go`).
|
||||
- The commits `0fc909d`, `5a446d6`, `fa91e2a`, `b6e6736`, `3cb2252`, `48e2642`, `03b8cc2`, `2e21084`, `768447b`, `cc80ea7`, `c535975` and `22368c5` exist in their repositories.
|
||||
Reference in New Issue
Block a user