docs(18.1-06): complete phase 18.1 tests, gate and release plan (push-all)

This commit is contained in:
Jakub Zych
2026-10-14 21:13:47 +02:00
parent 5c2b0e1d8a
commit c1a65c9f22

View File

@@ -0,0 +1,403 @@
---
phase: 18.1-payment-gateway-subscriptions
plan: 06
subsystem: testing
tags: [subscriptions, stripe, cabana, taglist, reorder, gate, testcontainers, vitest, node-test, release]
# Dependency graph
requires:
- phase: 18.1-01
provides: cabana taglist field and flat reorder (tag v0.1.26 was held)
- phase: 18.1-02
provides: pgsub core (models, contracts, classes.Service, baseline)
- phase: 18.1-03
provides: golem15.pgstripesubscriptions (gateway, webhook, job, plan sync) and the subscription secrets
- phase: 18.1-04
provides: pgsub admin screens and customer API ("api-research" contract)
- phase: 18.1-05
provides: vue-paymentgateway-layer subscription pieces (tag v0.2.0 was held)
provides:
- "scripts/check-phase18.1.sh: fail-closed gate over four repositories (go, plugins, admin, layers, docs, postgres, --self-test, --all, PHASE181_REQUIRE_POSTGRES)"
- "unit and Postgres coverage of every behaviour plans 01 to 05 added, and the nine-row PHP test map"
- "fix: the localized list schema keeps reorder.flat"
- "validated 18.1-VALIDATION.md (nyquist_compliant)"
- "release: framework v0.1.26, layer v0.2.0, plugin requires on v0.1.26, all four repositories pushed"
affects: [consuming applications' subscription cutover, phase 20 import, /msd-verify-work 18.1]
# Actuals (#2632): chars/4 over the added lines of the four repositories (planning docs excluded)
actuals:
tokens: 60749
tasks: 5
commits: 11
commits: 11
plan_head_before: "summercms.go dbf8aaac74d6f94c5307ff86afda32d852fb3891; sm-paymentgatewaysubscriptions-plugin b72876253a33932109003fe211c228c5c89fcb44; sm-pgstripe-plugin 592b4741dedec2e7376ba7f2a94f8032cd04c3dc; vue-paymentgateway-layer 3dea9cd8cf82d46a9317e4f120c7544384c68ca3"
plan_head_after: "summercms.go 5c2b0e1d8a1929fa8154c262b288937690e69bce; sm-paymentgatewaysubscriptions-plugin 472b75bb420675d6a0e547622e9f28383a7092bc; sm-pgstripe-plugin baf3c55aa0cba87ae80babbb9b7e0711b9f6e86d; vue-paymentgateway-layer 30046f52590d69f3e6a396ac93b37bb425aa0894"
tech-stack:
added: []
patterns:
- "Phase gate requires tests by name per repository and package; the TAP detector also requires a minimum count of tests by name prefix"
- "A test-only export_test.go wrapper exposes an unexported helper to the external test package"
- "Concurrency proofs: two goroutines in overlapping transactions, exactly one claim or one fired event"
- "Open-transaction probe: pg_stat_activity 'idle in transaction' checked from a gateway fake at each call"
- "Golden schema transcribed from the PHP dump, not derived from the migration"
key-files:
created:
- scripts/check-phase18.1.sh
- ../sm-paymentgatewaysubscriptions-plugin/models/subscription_test.go
- ../sm-paymentgatewaysubscriptions-plugin/models/subscription_plan_test.go
- ../sm-paymentgatewaysubscriptions-plugin/models/feature_list_test.go
- ../sm-paymentgatewaysubscriptions-plugin/contracts/registry_test.go
- ../sm-paymentgatewaysubscriptions-plugin/lang_parity_test.go
- ../sm-paymentgatewaysubscriptions-plugin/apply_events_test.go
- ../sm-paymentgatewaysubscriptions-plugin/user_subscribable_test.go
- ../sm-paymentgatewaysubscriptions-plugin/baseline_test.go
- ../sm-paymentgatewaysubscriptions-plugin/service_api_test.go
- ../sm-paymentgatewaysubscriptions-plugin/admin_actions_test.go
- ../sm-paymentgatewaysubscriptions-plugin/customer_api_authz_test.go
- ../sm-pgstripe-plugin/subscriptions/decode_test.go
- ../sm-pgstripe-plugin/subscriptions/job_test.go
- ../sm-pgstripe-plugin/subscriptions/sync_plan_test.go
- ../sm-pgstripe-plugin/subscriptions/gateway_test.go
- ../sm-pgstripe-plugin/subscriptions/php_map_test.go
- ../sm-pgstripe-plugin/subscription_secrets_test.go
modified:
- modules/cabana/list_schema.go
- modules/cabana/export_test.go
- modules/cabana/taglist_field_test.go
- modules/cabana/reorder_flat_test.go
- modules/cabana/catalog_smoke_test.go
- modules/cabana/README.md
- admin/tests/form/TaglistField.test.ts
- admin/tests/views/ReorderView.test.ts
- .planning/phases/18.1-payment-gateway-subscriptions/18.1-VALIDATION.md
- .planning/phases/18.1-payment-gateway-subscriptions/deferred-items.md
- ../sm-paymentgatewaysubscriptions-plugin/admin_smoke_test.go
- ../sm-paymentgatewaysubscriptions-plugin/README.md
- ../sm-paymentgatewaysubscriptions-plugin/go.mod
- ../sm-pgstripe-plugin/subscriptions/stripefake_test.go
- ../sm-pgstripe-plugin/README.md
- ../sm-pgstripe-plugin/subscriptions/README.md
- ../sm-pgstripe-plugin/go.mod
- ../vue-paymentgateway-layer/tests/subscriptions.test.ts
- ../vue-paymentgateway-layer/tests/subscribe.test.ts
- ../vue-paymentgateway-layer/tests/subscriptionComponents.test.ts
- ../vue-paymentgateway-layer/README.md
key-decisions:
- "Final push (D-22): human reply \"push-all\"; framework master and tag v0.1.26, pgsub and pgstripe main with summercms v0.1.26 required, layer main and tag v0.2.0 pushed; nothing force-pushed"
- "The localized list schema now carries reorder.flat (framework fix 0f09586, part of v0.1.26)"
- "The layer's check-git-install.sh stays open: no gate or release step runs it"
- "The gate never installs packages; a pnpm store mismatch fails the layer stage with the offline recovery command"
patterns-established:
- "Phase gate per phase: required names per repository and package, fail on skip, miss, todo, not ok or no tests, NOT RUN postgres loud"
requirements-completed: [SC-1, SC-2, SC-3, SC-4, SC-5, D-01, D-03, D-04, D-05, D-08, D-09, D-11, D-12, D-13, D-15, D-16, D-17, D-18, D-19, D-21, D-22]
coverage:
- id: D1
description: "Fail-closed phase gate over four repositories with a self-test of every detector"
requirement: SC-5
verification:
- kind: other
ref: "PHASE181_REQUIRE_POSTGRES=1 bash scripts/check-phase18.1.sh --all (all seven stages PASS)"
status: pass
human_judgment: false
- id: D2
description: "Framework taglist and flat reorder matrices, the localized reorder.flat fix, admin specs"
requirement: D-17
verification:
- kind: unit
ref: "modules/cabana/taglist_field_test.go#TestTaglistFieldValidate, #TestTaglistFieldHydrate; reorder_flat_test.go#TestReorderFlatNodes, #TestReorderFlatCompile"
status: pass
- kind: integration
ref: "modules/cabana/catalog_smoke_test.go#TestReorderFlatSmoke (partitioned subtest)"
status: pass
- kind: unit
ref: "admin/tests/form/TaglistField.test.ts; admin/tests/views/ReorderView.test.ts"
status: pass
human_judgment: false
- id: D3
description: "pgsub models, registries, event matrix, concurrent claim, user subscriber, golden baseline, service API, admin actions, customer API authorization, lang parity"
requirement: SC-1
verification:
- kind: unit
ref: "sm-paymentgatewaysubscriptions-plugin: TestSubscriptionChecks, TestPlanPrices, TestFeatureList, TestRegistry, TestLangParity"
status: pass
- kind: integration
ref: "sm-paymentgatewaysubscriptions-plugin: TestApplyEvents, TestClaimEventConcurrent, TestUserSubscribable, TestBaselineSchema, TestServiceAPI, TestAdminSyncActions, TestCustomerAPIAuthz"
status: pass
human_judgment: false
- id: D4
description: "pgstripe decode, job (STRIPE-006/007, duplicate, concurrent), plan sync, gateway, PHP map, subscription secrets"
requirement: SC-2
verification:
- kind: unit
ref: "sm-pgstripe-plugin/subscriptions: TestDecodeSubscription, TestDecodeInvoice, TestJobArgsMinimal"
status: pass
- kind: integration
ref: "sm-pgstripe-plugin/subscriptions: TestSubscriptionJob, TestSyncPlan, TestSubscriptionGateway, TestStripeSubscriptionReliability, TestSubscriptionAuthentication; root TestStripeSubscriptionSecrets"
status: pass
human_judgment: false
- id: D5
description: "Layer subscription client, flows and component specs (36 'subscriptions:' tests)"
requirement: D-14
verification:
- kind: unit
ref: "vue-paymentgateway-layer tests/subscriptions.test.ts, subscribe.test.ts, subscriptionComponents.test.ts"
status: pass
human_judgment: false
- id: D6
description: "Release: four repositories pushed, v0.1.26 and v0.2.0 tags on origin, plugin requires on v0.1.26"
requirement: D-22
verification:
- kind: other
ref: "git rev-parse <branch> == origin/<branch> in all four repositories; git ls-remote --tags origin v0.1.26 / v0.2.0"
status: pass
human_judgment: false
- id: D7
description: "Live Stripe test-mode checkout, the portal cancel_at question (D-21) and the admin and layer screens in a real browser"
verification: []
human_judgment: true
rationale: "Needs a real Stripe test account, a public webhook URL and a browser; listed as manual-only rows in 18.1-VALIDATION.md"
duration: 50min
completed: 2026-10-14
status: complete
---
# Phase 18.1 Plan 06: Tests, gate and release Summary
**Every behaviour of plans 01 to 05 now has a unit or Postgres test across the framework, pgsub, pgstripe and the layer. The nine PHP pgstripe tests map to named Go cases. `scripts/check-phase18.1.sh --all` is green with Postgres required. The tests exposed one framework bug (the localized list schema dropped `reorder.flat`), which is now fixed. On the human's "push-all" the phase was published as framework v0.1.26 and layer v0.2.0, and both plugins now require v0.1.26.**
## Performance
- **Duration:** about 50 min. Tasks 1 to 4 ran from 18:24:41Z to 19:09Z, then the checkpoint, then the release and close-out from 19:10Z to about 19:15Z.
- **Tasks:** 5 of 5.
- **Files:** 39 across four repositories, plus the planning docs.
## Task Commits
| Task | Repo | Commit | Message |
|------|------|--------|---------|
| 1 | summercms.go | `3cc4a5f` | test: fail-closed phase 18.1 gate with detector self-test |
| 2 | summercms.go | `0f09586` | fix: keep reorder.flat in the localized list schema |
| 2 | summercms.go | `323b6d6` | test: taglist and flat reorder matrices, partitioned flat smoke, admin specs |
| 2 | vue-paymentgateway-layer | `30046f5` | test: subscription client, flow and component coverage |
| 3 | sm-paymentgatewaysubscriptions-plugin | `1da437f` | test: pgsub unit and Postgres coverage |
| 3 | summercms.go | `6f71cd1` | test: require the pgsub tests in the gate |
| 4 | sm-pgstripe-plugin | `88f9125` | test: decode, job, plan sync, gateway, PHP map and secret coverage |
| 4 | summercms.go | `7790434` | test: require the pgstripe tests in the gate |
| 4 | summercms.go | `5c2b0e1` | docs: validated VALIDATION map and deferred items |
| 5 | sm-paymentgatewaysubscriptions-plugin | `472b75b` | chore: require summercms v0.1.26 |
| 5 | sm-pgstripe-plugin | `baf3c55` | chore: require summercms v0.1.26 |
**Plan metadata:** the docs commits that add this SUMMARY and the STATE/ROADMAP update, pushed after the tag. The tag is not moved.
## Task 5: release decision
**Reply (verbatim):** "push-all"
This is what was done, in the order of the push sheet. Nothing was force-pushed, and every repository was 0 commits behind its origin before its push.
**Framework**
- master pushed: `a377953..5c2b0e1`.
- Annotated tag `v0.1.26` created on `5c2b0e1` (tag object `03c33dd`) and pushed.
**pgsub**
- Commit `472b75b` raises the require to `git.golem15.com/golem15/summercms v0.1.26`; the replace onto `../summercms.go` is unchanged.
- `go mod tidy` resolved through the replace, so no proxy fetch was needed (default GOPROXY, no GONOSUMDB) and go.sum is unchanged.
- vet and the `-short` tests pass.
- main pushed: `aad4647..472b75b`.
**pgstripe**
- Commit `baf3c55` makes the same bump, with the same tidy, vet and `-short` results.
- main pushed: `0313820..baf3c55`.
**Layer**
- main pushed: `ac2b960..30046f5`.
- Annotated tag `v0.2.0` created on `30046f5` (tag object `f1107fa`) and pushed.
**Check:** after the pushes, each local branch equals its origin branch:
| Repository | Branch | Hash |
|------------|--------|------|
| summercms.go | master | `5c2b0e1` (before this SUMMARY's docs commits, which are pushed afterwards) |
| pgsub | main | `472b75b` |
| pgstripe | main | `baf3c55` |
| layer | main | `30046f5` |
`git ls-remote --tags` shows `v0.1.26` on the framework origin and `v0.2.0` on the layer origin.
## Tests added
**Framework (cabana, admin)**
- Docker-free tests:
- `TestTaglistFieldValidate`: 19 accepted and 17 refused cases, both modes, the locale, multiple fields.
- `TestTaglistFieldHydrate`: 12 stored shapes.
- `TestReorderFlatNodes`: flat parent 422, duplicate and zero id 422, missing, extra or swapped node 409, a 200-node flat list, and the tree contrast.
- `TestReorderFlatCompile` now also checks the localized list schema.
- Postgres test: the subtest `TestReorderFlatSmoke/partitioned`, over the new in-memory `acme.shelf` fixture.
- vitest:
- 9 TaglistField coverage specs: script markup, Backspace, blur, paste, datalist, customTags, comma string mode, array mode, aria.
- 2 flat ReorderView specs: the keyboard and the three drop zones.
**pgsub**
- Docker-free tests: `TestSubscriptionChecks`, `TestPlanPrices`, `TestFeatureList`, `TestRegistry`, `TestLangParity`. `TestLangParity` checks keys, `:placeholders` and every key the sources name; a planted placeholder mismatch made it fail.
- Postgres tests:
- `TestApplyEvents`: 272 cases (status pairs times cancel flags) against an independent copy of the PHP rule, plus deleted, invoices and refused updates.
- `TestClaimEventConcurrent`: two overlapping claims, five rounds, plus a rollback.
- `TestUserSubscribable`.
- `TestBaselineSchema`: golden names from the PHP dump, jsonb columns, RESTRICT, a re-run, rollback then migrate, the users column only when missing.
- `TestServiceAPI`: a probe gateway checks for open transactions and committed customer ids at each call; a sanity subtest proves the probe sees an open transaction.
- `TestAdminSyncActions`.
- `TestCustomerAPIAuthz`: routes × anonymous, malformed and admin tokens, the owner and another user. It covers the byte-exact none answer and ignored redirect and subscriber fields.
**pgstripe**
- Docker-free tests: `TestDecodeSubscription`, `TestDecodeInvoice`, `TestJobArgsMinimal` (a fixed key list, no PII).
- Postgres tests:
- `TestSubscriptionJob`: STRIPE-006 rollback through a listener that fails on demand, STRIPE-007 skips, duplicate and concurrent jobs, both shapes.
- `TestSyncPlan`.
- `TestSubscriptionGateway`.
- `TestStripeSubscriptionReliability` and `TestSubscriptionAuthentication` (the PHP map).
- Root `TestStripeSubscriptionSecrets`.
- `TestSDKPin` remains the STRIPE-003 case.
**Layer:** 9 more `subscriptions:` tests (36 in total):
- refusals passed through unchanged, and the `usePlans` key;
- a retry after a refusal, and the buttons' copy before a click;
- the currency prop;
- the confirmed 404 none answer end to end;
- the Cancel visibility matrix;
- the manage flow with 401, 409, 422, 429 and 502;
- the cancel busy guard.
## PHP test map (pgstripe 8b47457)
| PHP | Go |
|-----|----|
| ReliabilityTest `test_stripe_006_idempotency_marker_after_job_success` :33 | `subscriptions.TestStripeSubscriptionReliability/stripe_006_marker_after_job_success` |
| ReliabilityTest `test_stripe_006_idempotency_marker_double_processing_prevented` :99 | `.../stripe_006_double_processing_prevented` |
| DataHandlingTest `test_stripe_001_api_keys_stored_as_plaintext` :28 | `TestStripeSubscriptionSecrets/stripe_001_not_plaintext` |
| DataHandlingTest `test_stripe_001_keys_encrypted_at_rest` :81 | `.../stripe_001_encrypted_at_rest` |
| DataHandlingTest `test_stripe_001_secret_keys_round_trip_encrypted_to_db` :153 | `.../stripe_001_round_trip` |
| DataHandlingTest `test_pay_10_lazy_decrypt_handles_plaintext` :227 | `.../pay_10_import_plaintext_and_encrypted` |
| DataHandlingTest `test_stripe_003_sdk_constraint_pinned` :278 | `TestSDKPin` (unchanged) |
| AuthenticationTest `test_pay_09_..._accepts_valid_sig` :176 | `subscriptions.TestSubscriptionAuthentication/pay_09_accepts_valid` (both URLs) |
| AuthenticationTest `test_pay_09_..._rejects_invalid_sig` :121 | `.../pay_09_rejects_invalid` (both URLs) |
| AuthenticationTest `test_stripe_002_*` :42, :74 | Phase 18 scope: `TestStripeAuthentication` (payment notification) |
## Gate results
`PHASE181_REQUIRE_POSTGRES=1 bash scripts/check-phase18.1.sh --all` passed every stage, and the Task 5 verify run (clean trees plus `--all`) printed `final-push-sheet-ok`.
| Stage | Result |
|-------|--------|
| self-test | PASS |
| go | 5 required framework tests |
| plugins | pgsub 9 named (models 3, contracts 1, root 5); pgstripe 7 named (root 4, subscriptions 3); vet and `-short` green in both repositories |
| admin | vue-tsc; vitest 1401 tests in 92 files |
| layers | 105 TAP tests, 0 failures; 36 named `subscriptions:` (minimum 36) |
| docs | TestDocsTree, docs:build, the OpenAPI check, the dist check and the phase 10 hygiene check |
| postgres | framework 3; pgsub 14; pgstripe 4 in the root and 7 in subscriptions |
No suite was NOT RUN.
## VALIDATION
`18.1-VALIDATION.md` is `status: validated`, `nyquist_compliant: true` and `wave_0_complete: true`. It covers every task of plans 01 to 06 and keeps three manual-only rows:
- the portal `cancel_at` check;
- a live test-mode checkout;
- the screens in a browser.
## Decisions Made
- Release per the reply "push-all", in the push sheet order. The tag was set on the code-complete HEAD `5c2b0e1`; the later docs commits are pushed without moving it.
- The STRIPE-002 payment-notification tests stay Phase 18 scope.
- The gate copies the Phase 18 detectors instead of sourcing them, and adds a check for the number of tests by name prefix.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] The localized list schema dropped `reorder.flat`**
- **Found during:** Task 2. This was the deferred item from plan 18.1-04.
- **Issue:** `ListSchema.Localize` copied only the reorder title and partition, so `GET .../schema/list` answered a flat reorder without `flat: true`.
- **Fix:** `Flat` is now copied. The README and the docs already described the flag, so no doc change was needed.
- **Verification:** the RED subtest `TestReorderFlatCompile/the_localized_list_schema_keeps_flat` failed first; its RED evidence was checked as RED_EVIDENCE_OK. It passes after the fix.
- **Committed in:** `0f09586` (fix), then `323b6d6` (test).
### Adaptations
1. **Test-only wrappers in `modules/cabana/export_test.go`** (`CheckTaglistValues`, `HydrateTaglistFields`, `CheckReorderNodes`, `IsReorderStale`). The existing tests are in package `cabana_test`, which cannot call the unexported helpers.
2. **The partitioned flat case uses a fixture of its own.** It runs on the in-memory plugin `acme.shelf`, so the catalog fixture is unchanged.
3. **One shared source scan.** `TestAdminLangParity`'s scan moved into the helper `langUsedKeys`, which `TestLangParity` also uses. The behaviour of the existing test is unchanged.
4. **A price failure switch in the package Stripe fake** (`failPrices`), so a test can fail the price step after the product step.
5. **`subscriptions/decode_test.go` is an internal test**, because the decoders are unexported.
6. **The STRIPE-006 failure is injected through a listener** that fails on demand, registered by the test. The failure then happens after Apply saved the row, which proves that the row and the marker roll back together.
7. **The gate's layer stage never installs.** If pnpm aborts on the store mismatch, the stage fails and prints the offline, frozen-lockfile recovery command.
8. **A success-URL case with a fragment was dropped.** Go and PHP both append `?session_id` after a `#fragment`. That is PHP parity, and it is left untested rather than pinned.
9. **Most coverage tasks had no failing test first.** The tdd="true" coverage tasks tested behaviour that already existed, so only the `reorder.flat` defect produced a failing test before the fix. The plan is `type: execute`, so the plan-level TDD gate does not apply.
**Total deviations:** 1 auto-fixed (Rule 1) and 9 adaptations.
**Impact on plan:** test-side plumbing only, plus one framework bug fix. No scope or dependency was added.
## Deferred items
| Item | Status |
|------|--------|
| `reorder.flat` in the localized list schema | resolved by `0f09586` |
| pnpm store-dir mismatch (watch item) | resolved: the gate ran the layer suite green from a clean shell, and the layer stage never installs |
| `vue-paymentgateway-layer/scripts/check-git-install.sh` targets vue-journal-layer | **still open**: no gate or release step runs it |
Because of the last item, an install check of the published `v0.2.0` tag needs a quick task on the layer first.
## Issues Encountered
- **The tool layer unescaped `<` in written test literals.** The affected expectations were rewritten with explicit bytes.
- **An interactive `cp` alias blocked a file restore after a mutation check.** The file was restored with `command cp -f` and confirmed clean.
## Threat Flags
None. The plan added tests, a one-line framework fix, two go.mod require bumps and the releases.
- **T-18.1-32:** the gate requires every test by name, and `--self-test` proves its detectors.
- **T-18.1-33:** every Stripe call in the tests goes to the fakes, and the secrets in tests are test-only literals.
- **T-18.1-34:** the full gate ran before the human's reply, and the pushes followed the resolvable order without a force push.
- **T-18.1-SC:** no package was installed.
## Known Stubs
None.
## User Setup Required
None.
## Next Phase Readiness
- The phase is published.
- Applications can require `git.golem15.com/golem15/summercms v0.1.26`.
- They can pin `vue-paymentgateway-layer#v0.2.0`.
- They can build pgsub and pgstripe from main.
- One limitation remains: pgstripe requires `sm-paymentgatewaysubscriptions-plugin v0.0.0` with a replace, and pgsub has no tag. Building from git without the replace needs a pgsub tag or a pseudo-version, the same as the existing sm-paymentgateway-plugin pattern.
- Next: `/msd-verify-work 18.1` with the manual-only rows: a live Stripe test-mode checkout, the portal `cancel_at` check and the browser pass.
---
*Phase: 18.1-payment-gateway-subscriptions*
*Completed: 2026-10-14*
## Self-Check: PASSED
- Files: the gate script, all 17 new test files and the validated VALIDATION map exist.
- Commits on their origin branches:
| Repository | Commits |
|------------|---------|
| summercms.go | `3cc4a5f`, `0f09586`, `323b6d6`, `6f71cd1`, `7790434`, `5c2b0e1` |
| pgsub | `1da437f`, `472b75b` |
| pgstripe | `88f9125`, `baf3c55` |
| layer | `30046f5` |
- Tags: `v0.1.26` is on the framework origin and `v0.2.0` on the layer origin.