docs(19.4.1): add phase verification report

This commit is contained in:
Jakub Zych
2026-10-20 20:20:55 +02:00
parent a4c94d126f
commit 9f65186d42

View File

@@ -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 `<DemoBanner compact>`. `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)_