From 9f65186d420ce83c279b29a9efd91a04cd256998 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Tue, 20 Oct 2026 20:20:55 +0200 Subject: [PATCH] docs(19.4.1): add phase verification report --- .../19.4.1-VERIFICATION.md | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-VERIFICATION.md diff --git a/.planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-VERIFICATION.md b/.planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-VERIFICATION.md new file mode 100644 index 00000000..e99b6b4d --- /dev/null +++ b/.planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-VERIFICATION.md @@ -0,0 +1,246 @@ +--- +phase: 19.4.1-early-public-demo-on-demo-summercms-io +verified: 2026-10-20T18:25:00Z +status: human_needed +score: 49/50 must-haves verified (5/5 roadmap success criteria, 44/45 plan truths; 1 present, behaviour-unverified) +covered_files: + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-01-PLAN.md + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-01-SUMMARY.md + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-02-PLAN.md + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-02-SUMMARY.md + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-03-PLAN.md + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-03-SUMMARY.md + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-04-PLAN.md + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-04-SUMMARY.md + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-05-PLAN.md + - .planning/phases/19.4.1-early-public-demo-on-demo-summercms-io/19.4.1-05-SUMMARY.md +covered_digest: "v2:sha256:14a73f35757bf60f18321a64aaeeb6330522c550ebd08e4a3e383cdd309852f0" +# Implementation lives in sibling repositories outside this project root, so the +# fingerprint tool cannot cover it. Verified heads: sm-demo-app ea832fa, +# vue-demo-app 045bcf8, sm-user-plugin 1fc0ce7 (v0.1.3 = 7b9faa3), +# sm-journal-plugin 48d6593 (v0.2.1 = fe1bbc2), sm-presentations-plugin 5bcbaaf (v0.1.1). +behavior_unverified: 1 +overrides_applied: 0 +behavior_unverified_items: + - truth: "D-22: an open tab of the site shows a non-blocking 'A new version is available, reload' banner once a newer build is deployed, and reloads only when the user clicks." + test: "Open https://demo.summercms.io in a tab, leave it open across the next redeploy, then switch away and back to the tab (or wait up to 5 minutes)." + expected: "The update banner appears without a reload; the page reloads only after clicking Reload." + why_human: "Only isNewerBuild is unit-tested. The app:manifest:update hook, the visibilitychange re-check against builds/latest.json and UpdateBanner are wired, but no test or recorded UAT drives the transition across a real deploy." +human_verification: + - test: "Site-cache purge token in the public page payload: decide on rotation and the fix." + expected: "Either (a) the token is rotated on rome (.env SUMMER_SITE_CACHE__PURGE_TOKEN and site.env NUXT_SITE_CACHE_PURGE_TOKEN) and the site stops publishing it, or (b) the exposure is explicitly accepted." + why_human: "Every server-rendered page of demo.summercms.io carries the server-only purge token in the Nuxt payload (site-config key cachePurgeToken). nuxt-site-config, pulled in through the layers' @nuxtjs/seo, reads every NUXT_SITE_* environment variable as site config, and the layer's documented env name NUXT_SITE_CACHE_PURGE_TOKEN collides with that prefix. It is not exploitable from outside today: nginx refuses POST to Nuxt (live POST /api/__site__/cache/purge answers 403) and the route refuses proxied requests. Still, a secret is published. Fixing it touches vue-journal-layer, a shared layer, so it needs your call." + - test: "D-22 reload-on-deploy banner after a real redeploy (see behavior_unverified_items)." + expected: "The banner appears in an already-open tab after a deploy; reload only on click." + why_human: "The transition across a deploy is not exercised by any test." + - test: "Confirm the judgment-tier prohibitions (table 'Prohibitions' below)." + expected: "You agree with the verifier's non-authoritative verdicts. All are consistent with the SUMMARYs and the code. The sm-bm-app and presentations-plugin edits were your summer-theme request." + why_human: "Judgment-tier prohibitions (process order, 'no file modified', 'no secret printed in a log') are not machine-provable from the repositories alone." +--- + +# Phase 19.4.1: Early public demo on demo.summercms.io Verification Report + +**Phase Goal:** Clients can be shown a live demo at `demo.summercms.io` now, before the chat family lands: `sm-demo-app` with today's 16 shared plugins runs on rome, its public Nuxt site shows the plugins working through prepared demo accounts, and the admin is reachable at a public URL (`/smbackend`) behind a login, with credentials given out by Golem15 on request. +**Verified:** 2026-10-20T18:25:00Z +**Status:** human_needed +**Re-verification:** No, initial verification + +The goal is achieved in the code and on the live site. Status is `human_needed` rather than `passed` for three reasons: a security finding needs a decision (the purge token is published in the page payload), one behaviour truth has no test across a real deploy (D-22), and the judgment-tier prohibitions need your sign-off. There are no gaps. + +User decisions honoured, not gaps: the admin is public at `/smbackend` with a login (it replaced the private admin), the `summer` deck theme (it replaced atlas, and its edits to sm-bm-app and sm-presentations-plugin were requested), and the decks' plugin pins. Manual UAT approved during execution counts as passed: the Stripe checkout (02 Task 7), the phone remote (04 Task 7), the OIDC screenshots (01 and 04) and the contact form reaching G15Office (task ztqpldsk). + +## Goal Achievement + +### Roadmap Success Criteria + +| # | Success criterion | Status | Evidence | +|---|-------------------|--------|----------| +| SC-1 | sm-demo-app pushed to git.golem15.com; go.mod requires tagged framework and every shared plugin; release build without go.work | ✓ VERIFIED | `git ls-remote origin master` = ea832fa = HEAD (vue-demo-app 045bcf8 = HEAD). go.mod requires summercms v0.3.0 and the 14 plugin modules of summer.yaml at tags, with no `replace` and no nested `plugins/demo/go.mod`. `check-pins.sh`: ok (14 modules), run now. `build.sh` line 82 builds with `GOWORK=off … GOOS=linux GOARCH=amd64`. The gate log has `build: ok release 795ab5666f0e`. `TestGoModRequiresTags` PASS (run now). | +| SC-2 | demo.summercms.io serves Nuxt and Go from rome (nginx, supervisor, Postgres, TLS); DEPLOY.md covers build/upload/release/rollback; /backend refused; "contact Golem15" CTA | ✓ VERIFIED | `scripts/check-live.sh https://demo.summercms.io` exit 0, run now. My own curl: `/backend`, `/backend/` and `/backend/auth` answer 404; `/smbackend` 200 with noindex and HSTS; `/smbackend/api/v1/auth/me` and the admin list API answer 401 without a session; `/oauth/token` and `/.well-known/openid-configuration` 404. The nginx vhost has `location ^~ /backend { return 404; }`, a Go allow-list, `/ws` to Centrifugo and no subscribe-proxy location. DEPLOY.md has Build, Upload, Release, Verify, Rollback, Reset and Secrets sections. The CTA "Want to see the backend? Contact Golem15" is on `/` (check-live) and links to `/contact`. | +| SC-3 | Self-registration (Turnstile, rate limits, no credentials shown); banner on every page (reset, Stripe test); configurable reset job, disabled | ✓ VERIFIED | `golem15.user.turnstile: closed` in config/golem15.user.yaml. The user plugin's `turnstileGuard` appends `turnstile:{mode}` to login and register only. Live token-less register answers 422 `turnstile_failed`. `TestTurnstileRoutes` and `TestDemoTurnstile` PASS (run now). The banner text is present on /, /login, /register, /contact, /faq, /subscriptions, /blog and /quizzes, with no pre-filled password and no demo email on any of them (curl now). The client-only deck pages carry ``. `reset.enabled: false` and `at: "04:00"` in plugins/demo/config/config.yaml; `Schedule()` returns nothing unless enabled. `TestResetKeepsPlans` PASS (run now, Docker). | +| SC-4 | Guided tour (6 cards) in the summercms.io look, EN/PL; bm presentation system (public, speaker, phone remote, own decks) on own Centrifugo; roadmap deck from ROADMAP.md with feature links and OIDC screenshots; quizzes keep logged-in attempts | ✓ VERIFIED | The live `/` lists Journal, Presentations, Quizzes, Payments, FAQ and Accounts; with the pl cookie these are Blog/Prezentacje/Quizy/Płatności/FAQ/Konta. Live decks `roadmap` and `roadmap-pl` (theme summer, 30 slides each) have their sections in D-20 order (Roadmap, Features, Migration from PHP, Cooperation with pxpx, The future). Both decks carry all 3 OIDC screenshots plus 2 admin screenshots, 0 anchors, demo and docs URLs as text, Lighthouse/WordPress/tide on the pxpx slide, MCP in the future section and 0 denylisted client names. The live WebSocket upgrade on /ws gives 101 (check-live). Pages exist for p/[slugHash], presentations/{index,new,[slug]/index,edit,pilot}, quizzes/{index,[slug]}. The quiz player calls `quizApi.attempts` and renders `AttemptList`. The phone remote was approved live in UAT. | +| SC-5 | New code has unit tests, delivered in the last plan | ✓ VERIFIED | The gate log (19:31, after the last commit in every repo) ends with `check-all: ok`. It ran go vet, go test without -short in the app, user and journal suites, vitest (14 files, 178 tests), sync-roadmap, pins, the release build, smoke, check-deploy, the local browser checks and check-live. Re-run now: vitest 178/178, `go test -short` green, and the Docker-backed TestSeedRoadmapUpsert, TestSeedLocales, TestResetKeepsPlans, TestDemoQuizList, TestDemoDeckWrites and TestDemoTurnstile all PASS. | + +### Plan Truths (merged; restatements of the SCs above are folded in) + +| Plan | Truth | Status | Evidence | +|------|-------|--------|----------| +| 01 | D-20/D-16 two decks, five sections in order, from the embedded ROADMAP.md | ✓ VERIFIED | Live deck JSON (above); `roadmap.Parse` and `Build` exist; `TestRoadmapDeckOrder` in the gate | +| 01 | D-16 reseed keeps public_hash, replaces slides, no duplicate images | ✓ VERIFIED | `TestSeedRoadmapUpsert` PASS (run now); live hashes still match those recorded in plan 02 | +| 01 | D-19 pxpx slide: Lighthouse GraphQL, WordPress via tide, no link, EN and PL | ✓ VERIFIED | Live decks: Lighthouse, WordPress and tide present, 0 anchors, both languages | +| 01 | D-20 no client name on any slide | ✓ VERIFIED | Live decks checked against the 6 denylist terms in names.go: 0 hits; `TestRoadmapNoClientNames` | +| 01 | D-06 visitor only from golem15.demo.visitor.*; skipped without an email; password never printed | ✓ VERIFIED | config.yaml has empty defaults; `TestSeedVisitorFromConfig` in the gate (plugins/demo/console) | +| 01 | D-20 `dev.sh present` and `snapshot` | ✓ VERIFIED | Both subcommands are in scripts/dev.sh; the user presented from them and approved (01 Task 4) | +| 01 | D-13 both decks have the same slide sequence with notes in their language | ✓ VERIFIED | 30 and 30 slides live; `TestRoadmapPolishTitles` | +| 02 | SC-1/D-03 module-mode go.mod, bin/demo linux/amd64, plugins/demo in the root module | ✓ VERIFIED | See SC-1 | +| 02 | D-03 plugin tags on pushed commits, none reused | ✓ VERIFIED | Submodules sit on tagged commits (`git submodule status` shows each tag); check-pins ok | +| 02 | check-pins confirms submodule HEAD = required version | ✓ VERIFIED | `check-pins: ok (14 modules)` run now | +| 02 | D-21 private repos with remote = HEAD | ✓ VERIFIED | ls-remote of both repos = HEAD (privacy is a server setting, taken from the SUMMARY) | +| 02 | SC-2/D-02 live on rome, own DB, cert, HSTS, DEPLOY.md | ✓ VERIFIED | See SC-2 | +| 02 | D-04 /backend 404; /oauth, /.well-known and /api/ not routed to Go; CTA | ✓ VERIFIED | Live curl and the vhost (see SC-2) | +| 02 | D-10 banner on every page with card 4242 | ✓ VERIFIED | See SC-3 | +| 02 | D-05 no credential shown, no admin link | ✓ VERIFIED | check-live `no_admin_link` on 7 pages; smoke.sh; curl scan for pre-filled passwords and demo emails | +| 02 | D-08 Stripe test mode, webhook at /webhook/stripe/subscriptions | ✓ VERIFIED | The live webhook route answers 405 to GET and 400 to an unsigned POST, so it reaches pgstripe's verifier; test checkout approved in UAT | +| 02 | D-16 release refuses a stale snapshot; each release runs demo:seed | ✓ VERIFIED | `build.sh:62` runs `sync-roadmap.sh --check` and dies if stale; DEPLOY.md Release runs demo:seed | +| 02 | D-06 credentials only on rome | ✓ VERIFIED | No production credential values in tracked files (scan); DEPLOY.md names keys only. Rome itself was not inspected (HTTP only). | +| 03 | D-07 opt-in golem15.user.turnstile, invalid value fails boot | ✓ VERIFIED | routes.go:46-56 and the user plugin's config default `""`; `TestTurnstileRoutes` PASS | +| 03 | D-05 /register and /login render the Turnstile widget; no credentials | ✓ VERIFIED | Live `data-turnstile-widget` on /register (check-live); curl scan | +| 03 | D-11 six cards; Payments/Płatności label | ✓ VERIFIED | Live home EN and PL | +| 03 | D-12 summercms.io look | ✓ VERIFIED | tokens.css copied; the user approved the look live (03 Task 8) | +| 03 | D-13 EN/PL with SSR from the cookie, including FAQ, menu and journal | ✓ VERIFIED | Live pl cookie gives Polish tour, nav and quizzes; `TestSeedLocales` PASS | +| 03 | Todo esrp4qtx: author subtitle, description, avatar | ✓ VERIFIED | Live `/_journal/api/v1/authors/summercms-team` returns subtitle Golem15, a description and an avatar URL; `TestJournalAuthorProfile` PASS | +| 03 | D-04 /contact posts to feedback; G15Office task; no mail | ✓ VERIFIED | contact.vue posts to `_feedback/api/v1`; G15Office task ztqpldsk approved in UAT | +| 03 | D-09 demo:reset --force keep-list; schedule off at 04:00 | ✓ VERIFIED | `TestResetKeepsPlans` PASS (asserts --force required, wipe, blob deletion, keep-list fingerprint and hashes); `TestResetSchedule` | +| 03 | D-22 reload banner after a new deploy, click-only reload | ⚠️ PRESENT_BEHAVIOR_UNVERIFIED | manifest-update.client.ts hooks `app:manifest:update` and re-checks `builds/latest.json` on visibilitychange (the live latest.json answers); UpdateBanner reloads only on click; `isNewerBuild` unit-tested. Nothing exercises the deploy transition. | +| 03 | D-03 user v0.1.3 and journal v0.2.1 required and pinned; others untouched | ✓ VERIFIED | go.mod and submodule tags; fonoteka.go pointer not bumped (05 SUMMARY) | +| 04 | D-14 bm views as client-only Nuxt pages | ✓ VERIFIED | nuxt.config.ts routeRules `/p/**` and `/presentations/**` ssr false; pages present | +| 04 | D-16 /roadmap redirects to the deck of the locale | ✓ VERIFIED | check-live: 302 to `/p/roadmap-{hash}` (en) and `/p/roadmap-pl-{hash}` (pl); `/_demo/api/v1/roadmap?locale=en` live | +| 04 | D-15 user decks: no CSS or context, unpublished, fixed themes | ✓ VERIFIED | `TestDemoDeckWrites` PASS (run now); live POST without a JWT gives 401 | +| 04 | D-17 own Centrifugo, /ws, lighthouse sync | ✓ VERIFIED | centrifugo.json.example (allowed_origins demo, namespace presentations, subscribe proxy); live /ws 101 with the right Origin and refused with a foreign one; remote approved in UAT | +| 04 | D-17 token route behind jwt.auth, realtime-owner and the bucket; subscribe never routed by nginx | ✓ VERIFIED | routes.go:82-83 `surf.Use("jwt.auth","presentations.realtime-owner",bucketRealtime)`; live token 401, subscribe GET 404 and POST not reaching Go; `TestRealtimeRoutes` PASS | +| 04 | D-18 anonymous quiz list without answers; members see score and attempts | ✓ VERIFIED | Live `/_demo/api/v1/quizzes?context=demo:pl` returns only slug, title, description and question_count; `TestDemoQuizList` PASS; [slug].vue uses attempts and AttemptList | +| 04 | D-13 EN and PL quiz and deck twins | ✓ VERIFIED | Live summercms-basics (en) and summercms-podstawy (pl) | +| 05 | SC-5 every VALIDATION row has a passing named test | ✓ VERIFIED | Gate log plus re-runs (see SC-5) | +| 05 | TestGoModRequiresTags uses modfile | ✓ VERIFIED | PASS (run now) | +| 05 | TestRoadmap*, TestSeedRoadmapUpsert | ✓ VERIFIED | Gate log; Upsert re-run PASS | +| 05 | TestSeedVisitorFromConfig, TestResetSchedule, TestResetKeepsPlans | ✓ VERIFIED | Gate log; KeepsPlans re-run PASS | +| 05 | TestDemoTurnstile, TestRealtime*, TestDemoDeckWrites, TestDemoQuizList | ✓ VERIFIED | Re-run PASS | +| 05 | bm library tests byte-identical under vitest | ✓ VERIFIED | vitest 14 files and 178 tests PASS (run now) | +| 05 | smoke.sh banner on every SSR page; deck-snapshot --expect-banner | ✓ VERIFIED | Gate log `smoke: ok`; script contents | +| 05 | check-live and check-all pass at phase end | ✓ VERIFIED | check-all.log ends `check-all: ok`; check-live exit 0 now | +| 05 | Todos: esrp4qtx done, ltcvqext pending with a progress note | ✓ VERIFIED | todos/done/journal-author-profile-fields.md; the pending todo has a "Progress (Phase 19.4.1)" section | +| 05 | D-01 19.4.1 between 19.4 and 19.5; Phase 20 builds on this demo | ✓ VERIFIED | ROADMAP.md lines 41-45 and Phase 20's goal ("adds the chat family to that live instance") | + +**Score:** 49/50 truths verified (1 present, behaviour-unverified: D-22) + +### Prohibitions + +| Plan | Prohibition | Tier | Verdict | +|------|-------------|------|---------| +| 01 | No credential in a committed file, slide or output | judgment | Holds (non-authoritative). The tracked-file scan finds only Cloudflare's published test keys and a local `admin@example.test` dev password in dev.sh; no deck carries credentials. | +| 01 | No client name reaches either deck | automated | ✓ Verified: live decks have 0 hits, and TestRoadmapNoClientNames enforces it | +| 01 | No file under sm-bm-app modified | judgment | Deviated by your request (the summer theme, commits 3a97157 and 1d446cc). Counts as honoured. | +| 01/02/03/04/05 | No push, tag or deploy before the go-ahead | judgment | Holds per the SUMMARYs (go-ahead recorded at each checkpoint); not machine-provable | +| 02 | No secret value committed or printed in a kept log | judgment | Holds for repositories and logs. **See the warning below:** the purge token is published in the live page payload, which is not "committed" but is an exposure. | +| 02 | /backend never reachable | automated | ✓ Verified (live 404 and the vhost) | +| 02 | No `replace` in go.mod | automated | ✓ Verified (TestGoModRequiresTags) | +| 02 | Framework not changed or tagged | judgment | Holds: no framework tag; go.mod stays on v0.3.0 | +| 03 | No mail for contact submissions | judgment | Holds: no listener, job or mailer in plugins/demo | +| 03 | No page shows or pre-fills a credential or links the admin | automated | ✓ Verified (check-live, smoke and curl scan) | +| 03 | Other apps' user and journal pins unchanged | judgment | Holds per SUMMARY 05 (fonoteka.go pointer not bumped) | +| 04 | nginx never routes /api/realtime/subscribe; Centrifugo secrets stay on rome | automated | ✓ Verified (vhost, check-deploy, live 404) | +| 04 | No user deck published or with CSS | automated | ✓ Verified (TestDemoDeckWrites) | +| 04 | No npm package except centrifuge@5.7.3; no Pinia, service worker or @fontsource | judgment | Holds: package.json gained only centrifuge 5.7.3 (and vitest 3.2.7 in plan 05, as planned) | +| 04 | No sm-bm-app or shared plugin file modified | judgment | Holds for plan 04 (sm-bm-app last commit 1d446cc is from plan 01) | +| 05 | Tests use no real secret or production DB | judgment | Holds: testcontainers Postgres and a fake siteverify | +| 05 | No production code change | judgment | Holds per SUMMARY 05; plan 05 commits are tests, scripts and docs | +| 05 | Test-only plugin commits untagged | judgment | Holds: 1fc0ce7 and 48d6593 carry no tag | + +### Required Artifacts + +| Artifact | Status | Details | +|----------|--------|---------| +| sm-demo-app/plugins/demo/roadmap/{roadmap,deck,curated,names}.go | ✓ VERIFIED | Present, tested, feed the live decks | +| sm-demo-app/plugins/demo/console/roadmap_deck.go, seed.go, reset.go | ✓ VERIFIED | Exercised by TestSeedRoadmapUpsert and TestResetKeepsPlans | +| sm-demo-app/plugins/demo/content (ROADMAP.md, oidc/*.png, screens/*.webp) | ✓ VERIFIED | Embedded; the images appear on the live decks | +| sm-demo-app/scripts/{build,check-pins,smoke,check-deploy,check-live,check-local,check-all,sync-roadmap,dev}.sh | ✓ VERIFIED | Run in the gate; check-live and check-pins re-run now | +| sm-demo-app/deploy/nginx/demo.summercms.io.conf, supervisor/*.conf, centrifugo.json.example | ✓ VERIFIED | Matches live behaviour | +| sm-demo-app/DEPLOY.md | ✓ VERIFIED | Setup, TLS, Stripe, Realtime, Build, Upload, Release, Verify, Rollback, Reset, Secrets | +| sm-demo-app/plugins/demo/{routes,realtime,decks_api,quizzes_api,schedule}.go | ✓ VERIFIED | Wired; live routes answer as specified | +| sm-demo-app/internal/demotest/postgres.go, root *_test.go | ✓ VERIFIED | Harness used by the Docker tests run now | +| fonoteka.go/plugins/golem15/user/routes.go (sm-user-plugin v0.1.3) | ✓ VERIFIED | Opt-in Turnstile guard | +| sm-journal-plugin/controllers/api/authors.go (v0.2.1) | ✓ VERIFIED | subtitle, description and avatar live | +| vue-demo-app: DemoBanner, BackendCta, TurnstileWidget, UpdateBanner, contact.vue, roadmap.vue, deck/*, remote/*, quizzes/*, manifest-update.client.ts, vitest.config.ts | ✓ VERIFIED | Present, wired, served live | + +### Key Link Verification + +| From | To | Via | Status | +|------|----|-----|--------| +| roadmap_deck.go | presentations classes.Import | `Replace: true` | ✓ WIRED (TestSeedRoadmapUpsert) | +| roadmap_deck.go | content.Roadmap | Parse then Build per locale | ✓ WIRED (live decks reflect ROADMAP phases incl. 19.4.1 and 19.7) | +| nginx vhost | supervisor programs | 127.0.0.1:8098 / 3098 / 8010 | ✓ WIRED (check-deploy) | +| build.sh | sync-roadmap.sh | `--check` before compile | ✓ WIRED (build.sh:62) | +| default.vue | DemoBanner | layout renders the banner | ✓ WIRED (banner on all SSR pages) | +| config/golem15.user.yaml | user routes.go | `turnstile: closed` | ✓ WIRED (live 422 turnstile_failed) | +| useJournalApi.ts | journalLocale.ts | translations[locale] | ✓ WIRED (journalLocale.test.ts; live PL) | +| plugin.go | schedule.go | `reset.enabled` gate | ✓ WIRED (TestResetSchedule) | +| realtime.ts | routes.go | /api/realtime/token then /ws | ✓ WIRED (live 401 and 101; remote UAT) | +| nginx `/ws` | Centrifugo | /connection/websocket | ✓ WIRED | +| centrifugo.json.example | routes.go | subscribe proxy on loopback | ✓ WIRED (check-local realtime; remote UAT) | +| roadmap.vue | /_demo/api/v1/roadmap | locale lookup | ✓ WIRED (live 302) | +| testmain_test.go | internal/demotest | `testing.Short()` gate | ✓ WIRED (short run skips; full run uses Docker) | +| check-all.sh | check-live.sh | final step | ✓ WIRED (log) | +| vitest.config.ts | bm tests | aliases | ✓ WIRED (178 pass) | + +### Data-Flow Trace (Level 4) + +| Artifact | Data | Source | Real data | Status | +|----------|------|--------|-----------|--------| +| Home tour / roadmap link | roadmap public_hash | `/_demo/api/v1/roadmap` (DB) | yes (live hash 2018bb…) | ✓ FLOWING | +| Roadmap decks | slides | ROADMAP.md snapshot, then demo:seed, then the presentations DB, then the public API | yes (30 slides each) | ✓ FLOWING | +| /quizzes | quiz list | `/_demo/api/v1/quizzes` (DB by context) | yes | ✓ FLOWING | +| Author card | subtitle, description, avatar | journal authors API (backend_users.metadata, system_files) | yes | ✓ FLOWING | +| Header/menu | menu items | `/_menu/api/v1` | yes | ✓ FLOWING | + +### Behavioral Spot-Checks + +| Behavior | Command | Result | Status | +|----------|---------|--------|--------| +| Live origin contract | `scripts/check-live.sh https://demo.summercms.io` | exit 0, `check-live: ok` | ✓ PASS | +| Pins | `scripts/check-pins.sh` | `check-pins: ok (14 modules)` | ✓ PASS | +| Short Go suite | `go vet` and `go test -short` (root, plugins/demo, internal) | all ok | ✓ PASS | +| Named Go tests | GoModRequiresTags, BootAllPlugins, AdminLoginThrottle, Realtime* (4) | PASS | ✓ PASS | +| DB behaviour tests | SeedRoadmapUpsert, SeedLocales, ResetKeepsPlans, DemoQuizList, DemoDeckWrites, DemoTurnstile | PASS (Docker) | ✓ PASS | +| Plugin tests | TestTurnstileRoutes (user), TestJournalAuthorProfile (journal) | PASS | ✓ PASS | +| Frontend | `pnpm test` (vitest) | 14 files, 178 tests | ✓ PASS | +| Admin needs login | curl `/smbackend/api/v1/auth/me`, the list API | 401 | ✓ PASS | +| Purge route not public | curl POST `/api/__site__/cache/purge` | 403 (nginx) | ✓ PASS | + +### Probe Execution + +Step 7c: no `scripts/*/tests/probe-*.sh` is declared or present. The phase's runnable checks (check-live, check-pins, the test suites) are run above. + +### Requirements Coverage + +REQUIREMENTS.md maps no ID to Phase 19.4.1 (`Requirements: TBD`), so there are no orphans. The local IDs SC-1 to SC-5 and D-01 to D-23 are covered in the tables above. D-18 is covered by plan 04; D-02, D-08 and D-21 by plan 02. + +### Anti-Patterns Found + +| File / location | Pattern | Severity | Impact | +|-----------------|---------|----------|--------| +| Live SSR payload on every page of demo.summercms.io (site-config `cachePurgeToken`) | Server-only secret published (nuxt-site-config picks up `NUXT_SITE_CACHE_PURGE_TOKEN`) | ⚠️ Warning (security) | The token gates the Nuxt site-cache purge. It is not usable from outside today: nginx denies POST to Nuxt and the route refuses proxied calls. It would become usable on any site that sets `NUXT_SITE_CACHE_ALLOW_PROXIED=true`. Root cause: the env name documented by the shared vue-journal-layer collides with nuxt-site-config's `NUXT_SITE_*` prefix. uat.grzybyfunkcjonalne.pl and summercms.io do not show the key. Value not recorded here. | +| sm-demo-app/plugins/demo/content/ROADMAP.md | Stale vs summercms master (one ROADMAP progress commit, a4c94d1) | ℹ️ Info | Expected and documented; `build.sh release` refuses until `sync-roadmap.sh` is run. The live deck is unaffected. | +| Header nav "Subscriptions" / "Subskrypcje" | Label | ℹ️ Info | The D-11 "Payments, never Subscriptions" rule is scoped to the tour card, which complies; the nav item names the /subscriptions page itself | +| ROADMAP.md Phase 20 goal | "The admin backend is not public" | ℹ️ Info | Outdated since the /smbackend decision; reword when Phase 20 is planned (already noted in SUMMARY 05) | +| sm-demo-app/scripts/dev.sh:42 | Local dev admin password constant | ℹ️ Info | Local `admin@example.test` only; production admin credentials live on rome | + +No TBD, FIXME or XXX debt marker exists in phase-modified code. The hits are in the embedded ROADMAP.md data and a `DUMMY_TOKEN` literal. + +### Human Verification Required + +#### 1. Purge token in the public page payload + +**Test:** Decide whether to rotate the site-cache purge token on rome and stop the site from publishing it (for example by renaming the layer's env variable away from the `NUXT_SITE_` prefix in vue-journal-layer and the demo's site.env, or by excluding the key from nuxt-site-config), or accept the exposure. +**Expected:** The token no longer appears in `curl -s https://demo.summercms.io/ | grep cachePurgeToken`, and the Go-side and Nuxt-side values are rotated together. Otherwise the exposure is recorded as accepted. +**Why human:** It is a shared-layer change with a cross-project effect (CLAUDE.md: no breaking changes in shared pieces without asking), plus a secret rotation on rome. + +#### 2. Reload-on-deploy banner (D-22) + +**Test:** Keep a demo tab open across the next redeploy, then refocus it or wait up to 5 minutes. +**Expected:** "A new version is available, reload" appears without a reload, and the page reloads only after the click. +**Why human:** Only `isNewerBuild` is unit-tested; the deploy transition is not exercised. + +#### 3. Judgment-tier prohibitions + +**Test:** Review the Prohibitions table. +**Expected:** You agree with the verdicts. +**Why human:** Process-order and "not modified" claims are not provable from code alone. + +### Gaps Summary + +No gaps. Every roadmap success criterion is met in the code and on the live site, confirmed by an HTTP run of check-live, independent curl probes, the gate log written after the last commit, and re-runs of the behaviour tests. The open items are a security warning that needs your decision (the published purge token), one behaviour without test coverage across a real deploy (the D-22 banner), and sign-off on the judgment-tier prohibitions. + +--- + +_Verified: 2026-10-20T18:25:00Z_ +_Verifier: Claude (msd-verifier)_