docs(18.1): phase complete - UAT 8/8, security 0 open, released framework v0.2.2, pgsub v0.1.1, pgstripe v0.1.1

This commit is contained in:
Jakub Zych
2026-10-16 21:08:33 +02:00
parent c02f2724ae
commit 92479ad559
4 changed files with 94 additions and 35 deletions

View File

@@ -139,6 +139,11 @@ Quality
- **Commits**: no co-author tags; one logical change per commit; planning docs and code in separate commits
- **Core plugin contracts**: the PHP user, blog, pages and payment plugins are shared across many projects; the Go ports must preserve their contracts and the PHP originals are not changed as part of this project
Validated in Phase 18.1: Payment Gateway Subscriptions
- [x] Generic subscriptions core (`sm-paymentgatewaysubscriptions-plugin`, pgsub v0.1.1) and pgstripe's subscription gateway, signed webhook and claim-first River job (pgstripe v0.1.1) on framework v0.2.2; admin plans/subscriptions screens, headless customer API and the layer's Pg subscription components
- [x] Live Stripe test-mode UAT 8/8 on a `summer build` demo app: checkout, single `activated` event, 409 on a second subscription, portal and site cancellation, admin sync and reorder; security audit 51 threats, 0 open (`18.1-SECURITY.md`)
## Key Decisions
| Decision | Rationale | Outcome |
@@ -166,6 +171,7 @@ Quality
| The local dev stack is the Nuxt app proxied to the Go binary, guarded fail-closed (`fonoteka.go/scripts/dev-stack.sh`), with dev-only seed and marker route gated on `SUMMER_ENV=local` only | Proves the port in daily use before cutover without touching the production origin; `dev` is refused because shared servers often use that name (review WR-05) | ✓ Good (Phase 14.2, 2026-10-12; gate e2e 17/0/0/0, manual UAT 1-11) |
| Framework serves public uploads itself (`storage.uploads.serve`, opt-in) with nosniff, a sandboxing CSP and attachment disposition for non-images | Local runs need WinterCMS upload URLs without nginx; the hardening keeps uploaded HTML or SVG from running on the app origin (review WR-03) | ✓ Good (Phase 14.2) |
| The opt-in request log (`http.access_log`) records method, matched pattern, status and duration, never the URL path | Share and invitation tokens travel in paths (review WR-01); an unported route shows as a 404 with an empty pattern | ✓ Good (Phase 14.2) |
| Subscription state comes from Stripe, not the event snapshot (Phase 18.1, D-21 amended) | Out-of-order webhooks regressed rows; the job re-retrieves the subscription under a per-subscription advisory lock, and a portal `cancel_at` counts as cancel at period end | ✓ Good (Phase 18.1 UAT, 2026-10-16) |
## Evolution
@@ -185,4 +191,4 @@ This document evolves at phase transitions and milestone boundaries.
4. Update Context with current state
---
*Last updated: 2026-10-12 after Phase 14.2 completion*
*Last updated: 2026-10-16 after Phase 18.1 completion*

View File

@@ -1156,7 +1156,7 @@ Plans:
4. A host test proves checkout, activation, `invoice.paid`, `past_due` with its grace period, cancel and duplicate-event idempotency against a Stripe fake.
5. The new code has unit tests, delivered in the phase's last plan.
**Plans:** 9/9 plans executed
**Plans:** 9/9 plans complete
Plans:
**Wave 1**
@@ -1385,7 +1385,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 →
| 16.3. Backend admin personal API tokens with scopes (INSERTED) | 4/4 | Complete | 2026-10-12 |
| 17. Blog companions | 6/6 | Complete | 2026-10-13 |
| 18. Payment Gateway | 8/8 | Complete | 2026-10-14 |
| 18.1. Payment Gateway Subscriptions | 9/9 | In Progress| |
| 18.1. Payment Gateway Subscriptions | 9/9 | Complete | 2026-10-16 |
| 19. Chat and ChatChannels | 0/TBD | Not started | - |
| 19.1. Chat satellites | 0/TBD | Not started | - |
| 19.2. Chat polish against WavePath clients | 0/TBD | Not started | - |

View File

@@ -1,19 +1,20 @@
---
msd_state_version: "1.0"
milestone: v1.0
current_phase: "19.3"
current_phase_name: Framework transport and infrastructure for WavePath (INSERTED)
status: Phase 19.3 plans complete; release and whole-phase docs pending in the phase 19.3 main session
stopped_at: Completed 19.3-06-PLAN.md (gap closure; release handed off to the phase 19.3 main session)
last_updated: "2026-10-16T09:58:01.923Z"
current_phase: 19
current_phase_name: Chat and ChatChannels
status: planning
stopped_at: Phase 18.1 complete, ready to plan Phase 19
last_updated: "2026-10-16T19:08:06.609Z"
last_activity: 2026-10-16
last_activity_desc: "Completed quick task 261016-0fm: Admin minors for v0.2.1"
state_head: 66fd4ae4b702373adc818324270337796f32d71a
last_activity_desc: Phase 18.1 complete, transitioned to Phase 19
state_head: c02f2724aeb21135f9077b8037bd72871e74a5e8
progress:
total_phases: 40
completed_phases: 31
completed_phases: 32
total_plans: 191
completed_plans: 188
completed_plans: 191
percent: 80
gsd_state_version: "1.0"
milestone_name: milestone
---
@@ -25,22 +26,22 @@ milestone_name: milestone
See: .planning/PROJECT.md (updated 2026-10-12)
**Core value:** An existing WinterCMS-shaped app can be ported plugin by plugin to a single Go binary without its frontend noticing: the PHP version's API contract is the acceptance test.
**Current focus:** Phase 19.3 — Framework transport and infrastructure for WavePath (INSERTED)
**Current focus:** Phase 19 — Chat and ChatChannels (Phase 19.3 is being worked on in its own session)
## Current Position
Phase: 19.3 (Framework transport and infrastructure for WavePath (INSERTED)) — EXECUTING
Plan: 6 of 6
Status: Phase 19.3 plans complete; release and whole-phase docs pending in the phase 19.3 main session
Last activity: 2026-10-16 - Completed quick task 261016-0fm: Admin minors for v0.2.1
Phase: 19 — Chat and ChatChannels
Plan: Not started
Status: Ready to plan
Last activity: 2026-10-16 — Phase 18.1 complete, transitioned to Phase 19
Progress: [████░░░░░░] 44%
Progress: [████████░░] 80%
## Performance Metrics
**Velocity:**
- Total plans completed: 79
- Total plans completed: 88
- Average duration: 21 min
- Total execution time: 104 min
@@ -62,6 +63,7 @@ Progress: [████░░░░░░] 44%
| 16.2 | 7 | - | - |
| 16.3 | 4 | - | - |
| 14.2 | 4 | - | - |
| 18.1 | 9 | - | - |
**Recent Trend:**
@@ -245,6 +247,9 @@ Progress: [████░░░░░░] 44%
| Phase 19.3 P04 | 60min | 5 tasks | 14 files |
| Phase 19.3 P05 | 49min | 7 tasks | 38 files |
| Phase 19.3 P06 | 40min | 6 tasks | 24 files |
| Phase 18.1 P07 | 20 min | 3 tasks | 18 files |
| Phase 18.1 P08 | 24 min | 4 tasks | 30 files |
| Phase 18.1 P09 | 60min | 5 tasks | 27 files |
## Accumulated Context
@@ -830,6 +835,14 @@ Recent decisions affecting current work:
- [Phase 19.3]: Phase 19.3 released as summercms v0.1.27 (Task 7 reply: release); sm-faq-plugin, sm-menu-plugin and sm-journal-plugin require v0.1.27 on origin; app plugin submodule pointers not bumped yet
- [Phase 19.3]: Phase 19.3 gate scripts/check-phase19.3.sh is fail-closed: a skipped, missing or red required test fails the stage; miniredis is test-only
- [Phase 19.3]: 19.3-06: release of v0.1.28 (tag, plugin require bumps, layer tags v0.3.0/v0.3.0/v0.5.0, pushes) handed off to the phase 19.3 main session with the whole-phase docs update; gap closure pushed and tagged nothing
- [Phase 18.1]: 18.1-07: D-21 superseded for event ordering (user decision 2026-10-14): the Stripe webhook job applies the subscription Stripe holds (re-retrieved outside any transaction, serialised by a per-subscription pg_advisory_lock via classes.WithSessionLock/SubscriptionLockKey); a Stripe 404 falls back to the event snapshot
- [Phase 18.1]: 18.1-07: ApplyDeleted on an unstored subscription returns classes.ErrSubscriptionNotStored so the job rolls its marker back and River retries the deletion
- [Phase 18.1]: 18.1-07: plan resolution order is exact price id, then stripe_product_id, then plan_code (any plan, active or not); contracts.Update gains ProductID and PlanCode
- [Phase 18.1]: 18.1-07: Service.SyncSubscription writes only gateway-owned columns of a FOR UPDATE re-read row (plus the plan via the optional contracts.SubscriptionRetriever), never the caller's whole row
- [Phase 18.1]: 18.1-08: checkouts bill only the plan's own Stripe price per currency (SubscriptionPlan.CheckoutPriceID, no cross-currency fallback); the yen is unsupported
- [Phase 18.1]: 18.1-08: customer checkouts go through Service.CheckoutGuarded (per-subscriber advisory lock, 409 for any open subscription) plus Stripe idempotency keys (10-minute window, parameter hash); users.stripe_customer_id is first-write-wins
- [Phase 18.1]: 18.1-08: framework pact.AdminBulkAction.Detached (opt-in, json:"-") runs a bulk action outside the cabana transaction; plan sync uses it; needs framework tag v0.1.27 at plan 09
- [Phase 18.1]: 18.1-09: framework release tag is v0.2.1 (v0.1.27 and v0.2.0 already on origin); first plugin tags pgsub v0.1.0 and pgstripe v0.1.0, pgstripe requires pgsub v0.1.0, replace lines kept
### Pending Todos
@@ -912,6 +925,6 @@ Items acknowledged and carried forward from previous milestone close:
## Session Continuity
Last session: 2026-10-15T21:01:19.601Z
Stopped at: Completed 19.3-06-PLAN.md (gap closure; release handed off to the phase 19.3 main session)
Last session: 2026-10-16T11:56:16.182Z
Stopped at: Phase 18.1 complete, ready to plan Phase 19
Resume file: None

View File

@@ -53,35 +53,55 @@
"name": "Admin Vue SPA",
"status": "complete"
},
{
"number": "10.1",
"name": "Runtime admin extension point (INSERTED)",
"status": "complete"
},
{
"number": "10.2",
"name": "Nest framework packages under modules and write run docs (INSERTED)",
"status": "complete"
},
{
"number": "11",
"name": "Jobs, realtime and search infrastructure",
"status": "in_progress"
"status": "complete"
},
{
"number": "11.1",
"name": "SummerCMS documentation for humans and AI agents",
"status": "in_progress"
"status": "complete"
},
{
"number": "11.2",
"name": "summercms.io Alpha 0.1 landing page on SummerCMS",
"status": "in_progress"
"status": "complete"
},
{
"number": "12",
"name": "Płytarium API — Collections and Albums",
"status": "complete"
},
{
"number": "12.1",
"name": "User plugin admin screens (INSERTED)",
"status": "complete"
},
{
"number": "12.2",
"name": "Admin form fields: date, file upload, relation editing with deferred binding (INSERTED)",
"status": "complete"
},
{
"number": "13",
"name": "Płytarium API — wishlist, notifications, CSV, credentials, public routes",
"status": "in_progress"
"status": "complete"
},
{
"number": "14",
"name": "Domain jobs and external integrations",
"status": "in_progress"
"status": "complete"
},
{
"number": "14.1",
@@ -93,10 +113,15 @@
"name": "Local Fonoteka frontend on local SummerCMS backend (INSERTED)",
"status": "complete"
},
{
"number": "14.2.1",
"name": "Translate plugin (INSERTED)",
"status": "complete"
},
{
"number": "14.3",
"name": "One-command app scaffolding (INSERTED)",
"status": "in_progress"
"status": "complete"
},
{
"number": "15",
@@ -106,22 +131,37 @@
{
"number": "16",
"name": "grzybyfunkcjonalne.pl on reusable blog views",
"status": "in_progress"
"status": "complete"
},
{
"number": "16.1",
"name": "Journal admin urgent fixes (INSERTED)",
"status": "complete"
},
{
"number": "16.2",
"name": "Plugin-provided field types and the Blog Editor formwidget (INSERTED)",
"status": "complete"
},
{
"number": "16.3",
"name": "Backend admin personal API tokens with scopes (INSERTED)",
"status": "complete"
},
{
"number": "17",
"name": "Blog companions",
"status": "in_progress"
"status": "complete"
},
{
"number": "18",
"name": "Payment Gateway",
"status": "in_progress"
"status": "complete"
},
{
"number": "18.1",
"name": "Payment Gateway Subscriptions",
"status": "pending"
"status": "complete"
},
{
"number": "19",
@@ -141,7 +181,7 @@
{
"number": "19.3",
"name": "Framework transport and infrastructure for WavePath",
"status": "pending"
"status": "in_progress"
},
{
"number": "19.4",
@@ -166,8 +206,8 @@
],
"next": {
"command": "/msd:progress --next",
"label": "Advance to the next step",
"reason": "Phase 18 of 40 · executing"
"label": "Advance to the next step (plan phase 19)",
"reason": "Phase 19 of 40 — needs a plan"
},
"updated_at": "2026-10-13T22:19:45.651Z"
"updated_at": "2026-10-16T19:08:06.654Z"
}