Current authority · 2026-07-25

Use the governed runtime; treat older screenshots as predecessor evidence

Local platform proof now requests the registry-governed cms-e2e bundle. Setup requires backend /health and /ready to return 2xx, and requires the CMS login and public web routes to return 2xx HTML. HTTP 4xx/5xx, redirects, wrong content types, and timeouts fail before browser tests run.

Evidence Source: .agents/specs/uat-production-readiness-convergence/ and focused typed-probe tests. Coverage Tier: unit/negative at this checkpoint; governed-bundle/browser evidence is tracked separately. Readiness State: CONDITIONAL. ARTIFACT_HONESTY_GAP: June screenshots and Cloud SQL/certificate/revision text below are dated provenance, not current topology. Current DB authority is Crunchy Bridge. Product readiness remains controlled by the active spec review.md.
User Manual · updated 2026-06-27

Operate the CMS Platform with evidence in view.

This manual now includes the 2026-06-27 production-deployment addendum: backend, CMS, and web are live on GCP Cloud Run run.app with Cloud SQL PG17, Upstash Valkey TLS, Secret Manager, and declarative Terraform IaC. Live shared-table RLS has also been provisioned on Cloud SQL and wired into the backend. The screenshots below are still the existing 2026-06-16 local visual captures plus the 2026-06-21 governed payment screenshot; this pass adds deployment status, not fresh production visual proof.

3Cloud Run services live
41RLS tables FORCE-enabled
3domain certs provisioning
0new screenshots
Addendum · 2026-07-15

Security-hardening round shipped, and the public web fixes deployed to production (no new screenshots)

Six fixes landed on main (v0.11.76–v0.11.82) from an autonomous, collision-free hardening loop, and the accessible web fixes were deployed to production Cloud Run. For operators this means: the public sites (www.austinchanglab.net, www.naelt.org.tw) now run cms-web revision cms-web-00052-xrf (@ v0.11.80) with three stored-XSS sinks closed, the Next.js-16 public-form bug fixed (forms/success/error routes now await their route params), and DB-driven config/locale render crashes guarded. The cms admin also received a confirmed-exploitable ComputedField eval-injection fix (v0.11.81), but the admin service has NOT been redeployed yet — that deploy is owner/cross-session-coordinated because its image also carries other sessions' in-flight admin UI.

What changed for operators

No operator screens or workflows changed shape — these are defensive fixes. If a content author had ever saved markup like <img onerror=…> in a content/event body or a computed-field dependency, it is now sanitized rather than executed. Rollback of the web deploy is instant to cms-web-00051-6rc.

Evidence boundary

Unit/component tier plus a production curl-smoke (health + platform/NAELT homepages + a content page all 200; JSON-LD well-formed). No new screenshots, no browser/UAT pass, and the cms-admin deploy is still pending owner coordination.

Evidence Source: PRs #771/#773/#775/#777/#779/#783/#785, tags v0.11.76–v0.11.82, prod revision cms-web-00052-xrf. Coverage Tier: unit/component + prod curl-smoke (web). Authority: .agents/specs/{ISSUE_LOG.md,NEXT_STEPS.md,RTM.md} (2026-07-15 rows), AGENTS.md CURRENT STATE SNAPSHOT.
Addendum · 2026-07-11

Manual, review, and showcase generation notes reconciled (no new screenshots)

This pass adds docs/SHOWCASE_GENERATION_GUIDE.md and connects it to the operator manual, executive review, docs hub, README indexes, and repo governance rollups. The operational rule is now explicit: the public showcase/docs hub stays benefit-led for potential users, this manual stays task- and source-manifest-oriented for operators, and the executive review stays evidence/claim-boundary-oriented for evaluators.

What operators should use

Use this manual for routes, workflows, source manifests, screenshots, and proof-tier labels. Use docs/index.html for the public docs hub, and use docs/SHOWCASE_GENERATION_GUIDE.md when changing showcase copy or links to manual/review artifacts.

Evidence boundary

This is a documentation/static-host governance update. It does not add screenshots, runtime probes, production deploy evidence, or UAT proof. Existing screenshot and runtime evidence tiers remain unchanged.

Evidence Source: guide/source-manifest reconciliation only. Coverage Tier: documentation/governance. Authority: docs/SHOWCASE_GENERATION_GUIDE.md, docs/MANUAL_GENERATION_GUIDE.md, docs/REVIEW_GENERATION_GUIDE.md, .agents/specs/{SPECS.md,NEXT_STEPS.md,RTM.md}. docs/* scan: no docs/*FEATURE*.md; docs/*SPEC*.md is secondary/historical unless fresher than active spec proof.
Addendum · 2026-06-30

Security & quality hardening + one-command developer onboarding (no new screenshots)

Three lanes shipped as PRs against dev (#162/#163/#164) closing out the local security release gate, activating secret-leak protection, and giving developers/agents a single onboarding command. These change backend/security/tooling/governance behavior, not operator screens, so the screenshots below still stand; this is a spec/PR-cited content note, not a fresh-runtime regen. For operators and contributors the practical effect is: the repo's own dependency and code scanners are all green and actually gate, no secret can be committed, and getting a correct local toolchain is now one command.

Gate · GREEN

Local security release gate is green end-to-end

Running bash scripts/test-security-ci.sh now reports Failures 0 with all five scanners present and passing — gosec, govulncheck, gitleaks, npm audit, and trivy — with nothing bypassed. trivy is now genuinely wired (pinned v0.71.2, vuln DB, scoped to source via trivy.yaml, --exit-code 1 so it actually gates) and reports 0 CRITICAL/HIGH in the repo's own dependency manifests.

Authority: .agents/specs/ISSUE_LOG.md (SECURITY-CI-RELEASE-GATE-001), PRs #162/#163.
Cleared · npm

npm advisories genuinely cleared

An unused lighthouse devDependency (which dragged in a vulnerable transitive chain) was removed and js-yaml bumped 3.14.2→3.15.0, so all four package directories report npm audit = 0 vulnerabilities. The fix is a real dependency change, not an audit suppression.

Authority: .agents/specs/ISSUE_LOG.md (SECURITY-CI-RELEASE-GATE-001), PR #162.
Fixed · gosec

Two real security fixes found by the now-working gosec

(a) The OAuth-state cookie's Secure flag was false in production because it was gated on config.Env=="production" while the canonical value is prd; it now uses env.IsProductionLike, so the cookie is marked Secure in production. (b) A MIME-sniffing reflected-XSS in the messaging webhook challenge echo is fixed with a text/plain content-type plus X-Content-Type-Options: nosniff.

Authority: .agents/specs/ISSUE_LOG.md (SECURITY-CI-RELEASE-GATE-001), PR #163.
Activated · secret guard

Secret-leak protection verified and activated

A fail-closed pre-commit gitleaks guard (installed by make install-hooks / make setup) blocks committing any secret — proven by a local gitignored cms/.env holding an AWS key being blocked at commit time (.gitignore alone does not stop git add -f). A full 3529-commit history review found NO real production secret.

Authority: .agents/specs/ISSUE_LOG.md (SECRET-SCAN-GUARD-001), PR #163.
Onboarding

One-command make setup for developers & agents

A new make setup (= install git hooks + the pinned security toolchain) is now the required first step for any contributor or agent. It is documented in CONTRIBUTING.md and steering doc #26, so a correct local toolchain no longer depends on tribal knowledge.

Authority: CONTRIBUTING.md, .agents/steering/26-developer-environment-setup-and-secret-guard.md, PR #164.
Tests · deterministic

Three pre-existing flaky/stale tests fixed

Three pre-existing flaky/stale tests were fixed deterministically, so the test suite is reliable rather than intermittently red. No product behavior changed.

Authority: .agents/specs/ISSUE_LOG.md (PREEXISTING-STALE-TEST-001), PRs #162/#163/#164.
Evidence Source: spec/PR-cited scanner runs + scripts/test-security-ci.sh (Failures 0) + a 3529-commit history review (no runtime screenshots this addendum). Coverage Tier: backend / security / tooling / governance; the 2026-06-16 local runtime captures still stand. Readiness State: the local security release gate is green and actually gates, secret-leak protection is active, and onboarding is one command — but NO new browser/UAT/deploy/live-provider proof this pass. Authority: .agents/specs/ISSUE_LOG.md (SECURITY-CI-RELEASE-GATE-001, SECRET-SCAN-GUARD-001, PREEXISTING-STALE-TEST-001), CONTRIBUTING.md, .agents/steering/26-developer-environment-setup-and-secret-guard.md, PRs #162/#163/#164. Branding: provisional NAELT CIS preserved (red #C53030 / gray #4A5568 / orange #DD6B20).
Addendum · 2026-06-29

Production-readiness hardening — fail-closed signup, live AI search, observability-ready (no new screenshots)

Two batches landed on 2026-06-29 that strengthen the production-readiness posture of the live Cloud Run + Crunchy stack: a UAT de-stub / de-mock / wire-up pass and a set of platform-operations lanes (observability, live GraphRAG, asset scan-path, worker). These change backend/security/operations behavior, not operator screens, so the screenshots below still stand; this is a spec/PR-cited content note. Several of the steps below are owner-gated (apply the monitoring, deploy the worker, run the backup restore-drill) and are called out as such.

Security · fail-closed

Signup site-DB provisioning fails closed in production

When a new tenant signs up, the platform provisions its site database with a privileged CREATE DATABASE. That path (ensureSiteDatabase) previously could fall back to default cms_user/cms_password and sslmode=disable. It now routes through the shared database.SiteDBCredentials() resolver, so in production it rejects default credentials and non-TLS connections — the same fail-closed contract as the rest of the per-tenant connection paths. Dev/local behavior is unchanged.

Authority: .agents/specs/uat-readiness-destub-wireup/review.md, PR #160.
LIVE · AI search

GraphRAG hybrid vector search is live on production data

The embedding model was corrected from the preview gemini-embedding-2-preview to GA gemini-embedding-001, and the showcase + naelt sites were re-ingested with 3072-dim Vertex vectors. A live authenticated graphRAGSearch now returns ranked results (e.g. showcase "content platform" → Atlas 0.70; naelt "被害人權益保護" → 0.76–0.83). The default semanticThreshold was lowered 0.6→0.4 to improve recall.

Authority: .agents/specs/crunchy-bridge-db-migration/review.md (REQ-CBM-006) + graphrag-threshold-default, PR #155.
Owner-gated · monitoring

Cloud Run observability IaC (apply pending)

Monitoring infrastructure-as-code now exists for the live stack: an email notification channel, 3 HTTPS uptime checks (api /health, www /, admin /login), and 3 alert policies (uptime-down, backend 5xx, TLS-cert-expiry). terraform validate passes and the plan is 7-add / 0-destroy. The apply is owner-gated — the monitoring is defined but not yet activated.

Authority: .agents/specs/cloud-run-observability/review.md, PR #158.
Reduced over-state · dead-code

Removed dead/mock surfaces from the contract

The over-stated AWS video pipeline (uploadVideoToAWS / checkVideoProcessingStatus, which always returned "not configured") was deleted from the GraphQL schema; the dead-mock GroupManagement.tsx and the unrouted stub PluginConfigurationManager were removed. Two grep-verified audits confirmed the codebase is already heavily hardened — most candidate "stubs" were rejected as real, wired code.

Authority: .agents/specs/uat-readiness-destub-wireup/review.md, PR #160.
Prerequisite

Asset file-scan can now scan object storage

The file-scan path was decoupled so it can scan object-storage (GCS) assets, not just local files — a prerequisite for the deferred asynq worker. The local storage path is unchanged.

Authority: .agents/specs/asset-object-storage-cloudrun/review.md, PR #156.
Owner-gated · worker

Worker runtime documented (no-op in prod today)

The asynq worker's production state is now documented: scanning is off, so the worker is effectively a no-op in production today, and the Cloud Run worker IaC is inert. Activating the worker is an owner-gated deploy decision.

Authority: .agents/specs/worker-runtime-deploy/review.md, PR #151.
Evidence Source: spec/PR-cited tests + two grep-verified audits (no runtime screenshots this addendum). Coverage Tier: backend / security / operations + terraform validate; the 2026-06-16 local captures + 2026-06-21 governed payment screenshot still stand. Readiness State: stronger production-readiness posture (security fail-closed, reduced over-state/mock surface, live AI search, observability-ready) — but monitoring apply, worker deploy, and the backup restore-drill remain owner-gated; no new browser proof this pass. Authority: .agents/specs/{uat-readiness-destub-wireup,cloud-run-observability,crunchy-bridge-db-migration,asset-object-storage-cloudrun,worker-runtime-deploy}/review.md, .agents/specs/{SPECS.md,NEXT_STEPS.md}, PRs #151/#155/#156/#158/#160. Branding: provisional NAELT CIS preserved (red #C53030 / gray #4A5568 / orange #DD6B20).
Addendum · 2026-06-29

Showcase & docs-site enhancement — a branded documentation hub

The docs site now has a front door. Previously docs.<domain>/ instantly redirected to the English manual; it now serves a branded Documentation Hub (docs/index.html) that presents the three core documents (User Manual EN / 繁中, Executive Review), a "See it live" card naming the showcase tenant as the canonical exemplar, and a condensed 8-capability map linking into this manual's Platform Capabilities section. The hub uses the same provisional NAELT CIS palette as this manual, so the three docs surfaces read as one product, and it bakes in no hard-coded production domain (live destinations are env-driven, matching the funnel rule).

What changed

  • New docs/index.html branded hub served at the docs-site root (infra/docs/Dockerfile updated to stop overwriting it with the redirect stub).
  • The showcase tenant is presented as the canonical live exemplar (page builder · /components gallery of all 11 components · /tutoring gated streaming + responsible-AI consultant · DB-driven donor PaymentForm), cross-linked with the /examples live-demos gallery.
  • This manual + the executive review gained this dated enhancement note; the #capabilities durable section is unchanged.

Operator note

The hub is a static page in the cms-docs nginx host. It becomes live on the docs domain only after a migration-free cms-docs image rebuild + gcloud run deploy (an offered owner follow-up). Browsing the repo, open docs/index.html directly — its links to the manual and review resolve relatively.

Evidence Source: repo artifacts (spec + committed hub + Dockerfile). Coverage Tier: documentation / static-host — NO new screenshots (the 2026-06-16 local captures + the 2026-06-21 governed payment screenshot still stand). Readiness State: committed + Dockerfile-served; live only after a cms-docs rebuild+deploy. Authority: .agents/specs/showcase-docs-enhancement/{requirements,design,tasks,review}.md, building on platform-landing-funnel, frontend-cms-showcase-examples, and tutoring-learning-showcase-demo. Branding: provisional NAELT CIS preserved (red #C53030 / gray #4A5568 / orange #DD6B20).
Addendum · 2026-06-28

Tutoring streaming & responsible-AI consultant (live on production)

The showcase tenant now demonstrates two new tutoring capabilities end-to-end and live on production at www.austinchanglab.net/site/showcase/tutoring. As an operator you can open that page and: (1) press Play preview on a public “Sample teaching” clip — it streams a real, signed video, while paid lessons stay locked; (2) chat with the consultant — it answers common questions, and when a visitor asks to book, asks pricing, or asks for a person, it hands off to a human teacher and records a notify-teacher inquiry that appears in the CMS form submissions.

“Sample teaching” — gated streaming (local_asset)

Public previews play inline from a short-lived HMAC-signed URL minted by requestTutoringPlayback and served by GET /api/tutoring/media/* (range-capable, fail-closed on tampered/expired links). Paid lessons render locked and never expose a URL. To swap in a SaaS provider later, add a driver (Cloudflare Stream is coded; mux/gcp/aws/azure are credential-gated).

Showcase tutoring Sample teaching: a public preview video playing inline, with paid lessons locked
Evidence Source: live prod (Crunchy + Cloud Run), Playwright capture. Coverage Tier: browser-full-integration + live signed playback. Readiness State: LIVE; cloud providers are credential-gated follow-ups (ISSUE_LOG).

Consultant chat with responsible-AI human handoff

A deterministic curated_faq assistant answers a prospective student's first questions; on booking/pricing/explicit-human intent or low confidence it escalates to a human teacher (creating a notify-teacher inquiry) instead of guessing. The conversation shows the bot's reply and a clear “a human teacher has been notified” confirmation.

Tutoring consultant chat: bot answers an FAQ, then escalates a booking request to a human teacher
Evidence Source: live tutoringConsult on prod, Playwright capture. Coverage Tier: browser-full-integration. Readiness State: LIVE; deterministic FAQ (no live LLM claimed).
Authority: .agents/specs/tutoring-streaming-providers/review.md, PR #133. Two latent production bugs were found + fixed en route (StrictCORS not reading CORS_ORIGINS; missing CSP media-src). Residual: live cloud providers + tutoring_teacher_profiles/course_profiles prod migration tracked in ISSUE_LOG.md.
Addendum · 2026-06-27

Production deployment status — GCP Cloud Run live, RLS wired (no new screenshots)

The platform now has a managed production deployment path: GCP Cloud Run for backend, CMS, and web; Cloud SQL PG17 via Auth-Proxy unix socket; Upstash Valkey over TLS; Google Secret Manager; and Cloud Run domain mappings for api, www, and admin. This manual update records the deployment state and operator boundary; it does not replace the existing local/manual screenshots.

LIVE · Cloud Run

Runtime and IaC authority

Backend boots in prd, GraphQL returned HTTP 200, and Cloud SQL + Upstash connections were observed. CMS and web are healthy on run.app. Terraform in infra/terraform/envs/gcp-cloudrun/ is the declarative authority; the live backend was imported into a 0-destroy reconcile.

LIVE · RLS provisioned

Shared-table RLS is no longer just local proof

The live Cloud SQL database now has cms_tenant_app as a non-superuser app role, ENABLE+FORCE RLS on 41 tables, and verify-rls PASS. Backend env/secrets now wire SCHEMA_TIER_APP_USER / password through Secret Manager.

Pending

Custom-domain and public-domain checks

Google-managed certificates for api, www, and admin are still provisioning. Until they finish, this manual does not claim end-to-end browser proof on custom domains or public-domain /mcp cross-tenant proof.

Not claimed

Deferred runtime surfaces

Worker runtime, AGE/GraphRAG, multi-region, and UAT sign-off remain outside this pass. Existing screenshots stay predecessor/local evidence, except the payment donor screenshot which keeps its governed browser-full-integration tier.

Authority: .agents/specs/production-deployment-topology/{review.md,adr/ADR-DEPLOY-003-gcp-cloudrun-cloudsql-cloudflare.md}, .agents/specs/{SPECS.md,NEXT_STEPS.md,RTM.md,ISSUE_LOG.md}, and infra/terraform/envs/gcp-cloudrun/. Branding: refreshed with the provisional NAELT CIS red/gray/orange palette.
Addendum · 2026-06-24

Resilience & fail-closed hardening (no new screenshots)

A run of backend/test-tier prod-readiness lanes shipped as PRs against dev (#107/#108/#110/#111/#112/#113), hardening the platform's failure behavior with executable chaos & fail-closed coverage and one real wire-up fix. These change backend behavior, not operator screens, so the screenshots below still stand; this is a spec/PR-cited content note, not a fresh-runtime regen. For operators the practical effect is: ingest/payment webhooks dedup safely under concurrent floods, asset upload & AI generation fail closed (no orphan records, no leaked DB connections), and AI-quota enforcement is proven on the real transaction path.

  • Webhook-flood dedup under race (REQ-RCC-006, PR #113) — proven on governed PostgreSQL: 8 concurrent IngestMessage for the same provider message → exactly one draft + one log persist; the rest dedup with no orphan. Closes the last adjacent-dependency chaos gap.
  • AI-generation transaction leak fixed (PR #110) — an invalid generation type used to return without rolling back its open transaction, slowly leaking pooled DB connections; now fixed and regression-guarded.
  • Fail-closed coverage locked (PRs #104/#107/#108) — transaction-scoped AI-quota enforcement, GraphRAG degrade-when-embedding-provider-down, and asset storage-down fail-closed (no orphan asset record).
  • Decision-logic coverage (PRs #111/#112) — recommendation ranking (behavior-weight × time-decay) and MCP connection-pool LRU eviction, both previously unguarded.
Evidence Source: spec/PR-cited tests (no runtime screenshots this addendum). Coverage Tier: unit + property + hand-killed mutation + self-contained & governed-PostgreSQL chaos/resilience. Readiness State: local proof only; NO governed-bundle browser, live external provider/OAuth/AI, hosted-Postgres, multi-node/real-image HA, DNS/TLS/deploy/UAT/production. Authority: .agents/specs/resilience-chaos-coverage/review.md, ISSUE_LOG.md (CHAOS-COVERAGE-EXT-001 Resolved), backend/test/TESTS.md, PRs #104/#107/#108/#110/#111/#112/#113.
Addendum · 2026-06-18

Production-readiness hardening (no new screenshots)

A backend/config-tier hardening lane shipped as PR #71 against dev, promoted from a repo-wide mock/stub/false-green audit (prod-readiness-wireup-hardening). It changes security/wire-up behavior, not operator screens, so the screenshots below still stand; this is a spec/PR-cited content note, not a fresh-runtime regen.

  • Per-tenant DB connections now fail closed in production. Site (tenant) database connections previously could fall back to default credentials and non-TLS in production; they now reject default user/password and require TLS (require/verify-ca/verify-full) when ENV=production, matching the platform-DB validation. Dev/local behavior unchanged.
  • AI provider keys are server-only. The CMS no longer reads NEXT_PUBLIC_*_API_KEY (which would ship secrets in the browser bundle); generation routes through the backend BYOK path, and the admin endpoint fallback now fails closed on Vercel-style production markers.
  • Real-dependency health proof. An empty placeholder test was replaced with a real chaos/resilience test proving a downed Postgres surfaces as HTTP 503 (unhealthy), bounded (no hang).
  • Docs reconciled. Module guides that under-stated implemented OAuth (Google/Facebook/Microsoft/Cognito + SAML + LDAP), storage (S3/GCS/Azure/R2/local), and real data-binding maturity were corrected.
  • Reliability proven under fault injection (PRs #72–#74). New chaos/resilience tests prove the platform degrades gracefully and bounded, never hangs: connection-pool exhaustion stays within its limit and recovers; a transient tenant-database outage is not cached and routing self-heals when the DB returns; and a hung AI provider fails with a bounded, typed error (finite client timeout).
  • Input sanitizer + fail-closed guards locked (PRs #75–#77). The name→tenant-ID/database-name sanitizer and the production credential/TLS fail-closed guards gained property-based + wiring tests so a future change can't silently weaken the only thing standing between user input and a database name, or re-open an insecure connection path.
Evidence Source: spec/PR-cited tests (no runtime screenshots this addendum). Coverage Tier: unit + property-based + hand-killed mutation (backend) + vitest/tsc (cms) + self-contained & governed-PostgreSQL chaos/resilience. Readiness State: local proof only; NO governed-bundle browser, live external provider/OAuth/AI, hosted-Postgres, multi-node/real-image HA, DNS/TLS/deploy/UAT/production. Authority: .agents/specs/{prod-readiness-wireup-hardening,resilience-chaos-coverage}/review.md, RTM.md, PRs #71–#80.
Addendum · 2026-06-21

Payments / Donations / E-commerce (browser-proven)

A generic, site-scoped payment / donation / e-commerce module shipped as PR #86 against dev (payment-plugin). Donation is one purpose preset of a generic Payment* core. Below is the live donor page rendered from DB-driven page data against the real backend.

Public donor form — PaymentForm (DB-driven)

The seeded NAELT /site/naelt/support/donate page renders the generic PaymentForm (the donation-form manifest preset) entirely from DB page data: preset amounts, custom amount, payer fields, and a provider-driven submit (Stripe redirect / 藍新金流 NewebPay form-post). No secrets touch the browser — the server resolves the site's BYOK provider by siteID.

Public donor PaymentForm rendered from DB-driven page data
Evidence Source: governed multi-tenant web env, real backend (MSW off), Playwright 2/2. Coverage Tier: browser-full-integration. Readiness State: PASS for DB→manifest→PaymentForm render + live seeded presets; NO live external-provider settlement.

CMS admin — Donation & Payment + Products & Services

The CMS Settings page gains a Donation & Payment tab (provider catalog, guided setup wizard, masked BYOK secret rotate, donation/order list + ezPay 電子發票 status) and a Products & Services tab (catalog CRUD, SKU variations, Udemy-style digital-content authoring: public preview vs paid). Admin GraphQL is RBAC-gated (site.payment.* / site.catalog.*).

Evidence Source: governed cms-e2e bundle, real /api/auth/login + real GraphQL (MSW off), Playwright 3/3 (live provider catalog, catalog tab, viewer-denied). Coverage Tier: browser-full-integration. Readiness State: PASS for admin tabs; admin-initiated refund + live settlement are residual (ISSUE_LOG.md PAY-REFUND-001).

Capability 8 — Payments / Donations / E-commerce. Generic public createPaymentCheckout(purpose) + provider-registry drivers (Stripe Checkout one-time + subscription; 藍新金流 NewebPay MPG 2.0 + 定期定額) + ezPay 電子發票 auto-issue + BYOK per-site secrets + product/service catalog + digital entitlements (auto-grant on paid, fail-closed access); the donation-form page-builder component renders the generic PaymentForm; one mount switch PAYMENT_MODULE_ENABLED. Authority: .agents/specs/payment-plugin/review.md, PR #86.

Worked Examples · 2026-06-18

Platform Capabilities — End-to-End Examples

Detailed, contract-grounded worked examples for the platform's authoring & runtime capabilities, using the showcase tenant as the canonical example (its home + a /components gallery page exercise all 11 page-builder components). Evidence tier: illustrative worked examples grounded in real GraphQL/route/manifest contracts — backend/CLI-lane evidence (commands & payloads), not live-demo screenshots; claim-cap per capability below. Authority for readiness remains each spec's review.md.

1. Page Builder (manifest-driven, DB-backed, no rebuild)

Pages are arrays of sections; each section names a component from the component-manifest SSOT (componentManifests GraphQL ← backend/internal/componentmanifest/manifests.json) plus its props. The CMS Pages builder edits sections and saves the whole section array via updatePageSections(id, sections: [JSON!]!). Public routes are force-dynamic, so adding an already-registered component to a page is DB-data-only — no image rebuild.

# Showcase home is built from these manifest-backed sections (backend/seeds/showcase-pages.json):
HeroSection · FeatureGrid · ContentList · EventSection · ImpactStats · CTASection
# The /components gallery page additionally demonstrates:
ResourceGrid · ContentSection · EnhancedContentList · EnhancedEventCalendar

# Save an edited page (full-array replace) — from the CMS page builder:
mutation { updatePageSections(id: "<pageId>", sections: [ /* the full ordered section array */ ]) { id } }

Claim-cap: manifest-validated section authoring; adding registered components is runtime-proven DB-only. NOT a new-component rebuild path. Authority: shadcn-component-library-a2ui-authoring/review.md.

2. AI Content Generation (backend BYOK)

Generation runs server-side through generateAIContent(prompt, providerId); the CMS never holds provider secrets in the browser (see the 2026-06-18 security hardening). Each site declares its own provider profiles (Google Vertex/Gemini, AWS Bedrock, Azure AI/OpenAI, OpenAI, Anthropic, TensorZero, OpenCode) with BYOK secret metadata + a usage ledger.

mutation { generateAIContent(prompt: "Draft a launch blog post about our new studio", providerId: "") { content model tokensUsed } }
# providerId "" = use the site's default profile; the backend resolves the BYOK key, never the browser.

Claim-cap: backend-routed generation contract; NO live external-provider call in this manual. Authority: site-byok-ai-provider-settings/review.md.

3. Content Management (types · draft/publish · submissions · RBAC)

Content is modeled by content types and moves through a draft → published → unpublished status lifecycle (status-scoped listing). Public forms submit into a governed submission workflow (submit → approve/reject) reviewed in the CMS. Every operation is permission-checked (e.g. site.content.*, site.social.read/manage).

# Public site form section (showcase /contact) → backend-issued submissionId → CMS review queue (approve/reject).
# Content list/detail pages render DB content by contentType (article/event) via ContentList/ContentSection.

Claim-cap: draft/publish breadth + submission workflow proven at unit/backend tier (LAUNCH-007); CMS review is backend-backed where the governed bundle runs.

4. MCP & Agent-Friendly Surfaces

Each site exposes agent-friendly read surfaces: a Model Context Protocol endpoint at /mcp (list/get/search contents), an MCP discovery doc at /.well-known/mcp.json, and per-site + platform llms.txt. Non-VIP tenants' reads are confined by D2 Row-Level Security (routed non-superuser role) when SCHEMA_TIER_APP_USER is enabled.

# Discover + read a site over MCP (HTTP/JSON-RPC):
GET /.well-known/mcp.json        # server description
POST /mcp  → tools: list_contents(site) · get_content(site, slug) · search_contents(site)
GET /site/showcase/llms.txt      # agent-friendly site summary

Claim-cap: /mcp read path is backend-backed on the full local stack + RLS-confined as a non-superuser role on governed PG; cloud-deploy/UAT remains owner/infra (SPTR-LIVE-WIRING-001). Authority: schema-per-tenant-routing-rls/review.md.

5. Agent Skills (per-site declarative skill templates)

Sites define declarative skill/prompt templates (internal/skilltmpl; seeds skills-platform.json/skills-naelt.json) used to convert/optimize content — no script/code execution, no sandbox. Templates are versioned and managed via upsertSkillTemplate (create ⇒ v1; update ⇒ version bump; published templates immutable; cross-site fail-closed; @requirePermission("site.social.manage")).

mutation { upsertSkillTemplate(input: { id: 0, kind: "post-conversion", name: "LINE→blog", body: "..." }) { id version kind } }
# id:0 → creates version 1; id>0 → updates + bumps version.

Claim-cap: declarative template contract (no code execution); service + RBAC proven at enttest tier. Authority: messaging-ingest-social-publishing/review.md.

6. A2UI (agent-authored pages via the manifest)

The A2UI agent (backend/internal/adk) authors page-builder sections from the same component-manifest catalog, gated by a manifest-validation step + a design-contract no-invented-facts honesty boundary + an untrusted-content boundary. It proposes manifest-valid sections that you save through the normal updatePageSections path — so agent output is held to the exact same contract as human page-builder edits.

# A2UI flow: catalog prompt (componentManifests) → agent proposes sections → manifest validation gate
#            → design-contract honesty check → human review → updatePageSections(save).

Claim-cap: manifest-validated authoring + honesty boundary proven at unit/contract + dev-web browser tier; NO live-LLM round-trip claim. Authority: shadcn-component-library-a2ui-authoring/review.md.

7. Social Media — Complete Flow (ingest → AI convert → review → publish)

A platform-agnostic, integration-first pipeline (messaging-ingest-social-publishing): inbound messages arrive on a single canonical webhook, are normalized, converted to a post draft via the site's AI provider + a declarative skill template, queued for review, then published through pluggable publisher drivers. Site-scoped RBAC throughout (site.social.read/manage/publish/secret); secrets are site-BYOK.

# 1) INGEST — providers self-register; one canonical inbound route per platform/site:
POST /api/webhooks/messaging/{platform}/{site}    # e.g. line, telegram, whatsapp (signature-verified, fail-closed)

# 2) CONVERT — normalized message → post draft via SiteAIProviderResolver + per-site skill template (no sandbox).
# 3) REVIEW — draft sits in the CMS "Messaging & Social" review queue:
mutation { updatePostDraft(input: { id: "<draftId>", blocks: [...], metadata: {...} }) { id status version } }   # published drafts are immutable
# 4) PUBLISH — through a pluggable SocialPublisher driver (e.g. Facebook Graph):
mutation { publishPostDraft(id: "<draftId>") { id status } }   # @requirePermission("site.social.publish")

Claim-cap: repo-local integrated feature + governed cms-e2e backend-backed Messaging & Social browser smoke (T16.6); NO real LINE/Telegram/WhatsApp delivery, real Facebook publish, real OAuth, or live-AI quality. Authority: messaging-ingest-social-publishing/review.md.

Evidence Source: real GraphQL/route/manifest contracts + spec review.md (no new runtime screenshots — worked-example/contract tier). Coverage Tier: per-capability claim-caps above (mix of backend-backed, unit/contract, and runtime-backed). Readiness State: local proof only; NO hosted-Postgres, live external provider/OAuth/AI, DNS/TLS/deploy/UAT/production. Authority: .agents/specs/SPECS.md, RTM.md, and the cited per-spec review.md.
Addendum · 2026-06-17

Backend lanes shipped since this pass (no new screenshots)

Two backend/API-tier lanes shipped as PRs against dev after the 2026-06-16 runtime capture below. They add backend behavior, not new operator screens, so the screenshots on this page still stand; this is a spec/PR-cited content note, not a fresh-runtime regen.

  • Spawn now provisions the VIP database (PR #68). Previously a spawned VIP site was a control-plane record pointing at a database that did not exist (which is why earlier backup proofs had to hand-create cms_showcase). SpawnSite now creates the physical cms_<slug> database and migrates the schema into it for VIP sites; a non-VIP (SCHEMA/D2) site stays record-only by design. Fail-closed: if provisioning fails the site record is rolled back, so no dangling registry entry is left.
  • Messaging & Social draft/template editing API (PR #69). The updatePostDraft (edit a draft's blocks/metadata before publish — a published post is immutable) and upsertSkillTemplate (create, or update + version-bump, a skill template) GraphQL operations are now implemented, both gated on the site.social.manage permission. The in-CMS editor UI for these is a tracked follow-up.
Evidence Source: spec/PR-cited backend tests (no runtime screenshots this addendum). Coverage Tier: unit / property / mutation / enttest + governed-PostgreSQL integration (spawn). Readiness State: backend behavior proven locally; the governed-bundle backend-backed browser smoke for the Messaging & Social tab is a tracked residual. NO DNS/TLS/deploy/UAT/production. Authority: .agents/specs/RTM.md (2026-06-17 lanes 1+2 rollup), PRs #68/#69.

Getting Started / Starter Assets

Use the tracked seed/demo data first. Do not invent one-off manual examples.

Seed data (4 sites)

backend/seeds/{platform,naelt,showcase,tenant-demo}-*.json provide pages, content, submission types, themes, navigation, and skills. tenant-demo carries "tenancyTier":"schema" so it seeds as a non-VIP site. Seed with python scripts/ops.py db seed {platform|naelt|showcase|tenant-demo}.

E2E sample data

scripts/seed_e2e_data.py / scripts/seed_e2e_data.sql document canonical roles and sample tenants for repeatable test/demo flows (8 runtime users: platform_admin, site_admin, editor, viewer, MFA, two tenant admins). Passwords marked MOCK-DEV-ONLY-* are never production credentials.

Fresh assets

Current local screenshots and CLI transcripts live in docs/manual/assets/ as *-manual-2026-06-16.png and *-cli-2026-06-16.txt; the 2026-06-27 update adds deployment/RLS status without new screenshots.

Core Operator Flows

Follow these routes on the local runtime unless a production target has been explicitly named and provisioned.

CMS dashboard orientation

Open http://localhost:23000, authenticate through the configured flow, and verify dashboard scope. Platform admins see the platform control-plane shell with platform-site metrics (now 4 total sites), Recent Activity, and Quick Actions; site admins see a site-scoped workspace. Scope authority comes from the authenticated role and current site/tenant context, not the visual cues.

CMS platform dashboard current runtime screenshot
Evidence Source: live CMS runtime, CMS auth API cookie bootstrap. Coverage Tier: hybrid. Readiness State: PASS for dashboard visibility, not full browser-login proof.

Manifest-driven page builder (Pages)

Open http://localhost:23000/pages. The list is the entry to the manifest-driven page builder: per-page Layout (default / content-detail / event-detail), Status, search-by-title/slug, status and layout filters, and Preview/Edit/Delete per row. Adding an already-registered platform component to a page is a DB-data change — no frontend rebuild.

CMS Pages / manifest-driven page builder current runtime screenshot
Evidence Source: live CMS runtime screenshot (cookie bootstrap). Coverage Tier: hybrid. Readiness State: PASS for the seeded Pages surface; live page-save/publish and A2UI apply are separate workflow proofs.

Public platform homepage (shadcn / Tailwind v4)

Open http://localhost:23001/site/platform. The seed-backed marketing surface renders on the shadcn/Tailwind-v4 foundation: gradient hero, feature grids, onboarding steps, platform metrics, and CTA sections.

Platform homepage current runtime screenshot
Evidence Source: live public screenshot. Coverage Tier: local browser route evidence. Readiness State: PASS for styled public render; production visual/SEO/CDN remains separate.

Non-VIP site — tenant-demo (SCHEMA tier · D2 shared-table RLS) New

Open http://localhost:23001/site/tenant-demo. This fourth seeded site is non-VIP: its content lives in the shared platform tables and is isolated by PostgreSQL Row-Level Security keyed on the site slug (the "D2" model), rather than in a dedicated database. It renders identically to a VIP site — proving the isolation model is invisible to the end user.

tenant-demo non-VIP schema-tier public site current runtime screenshot
Evidence Source: live public screenshot of the seeded non-VIP site. Coverage Tier: local browser route evidence. Readiness State: PASS for seeded non-VIP render; live Cloud SQL RLS is now provisioned; custom-domain public read/MCP proof remains separate (see Host Preflight).

Showcase site (third seeded site)

Open http://localhost:23001/site/showcase — "Showcase Studio", a distinct DB-driven demo site proving multi-site delivery from one codebase via site config/theme/page seeds.

Showcase site home current runtime screenshot
Evidence Source: live public screenshot. Coverage Tier: local browser route evidence. Readiness State: PASS for seeded multi-site render.

Platform contact flow

Open http://localhost:23001/site/platform/contact to inspect the platform public submission route.

Platform contact current runtime screenshot
Evidence Source: live public screenshot. Route rendering only; submit/readback breadth is not upgraded by this screenshot.

NAELT volunteer registration

Open http://localhost:23001/site/naelt/volunteer/register. The seeded manifest renders Traditional Chinese field groups (志工報名) and required fields.

NAELT volunteer form current runtime screenshot
Evidence Source: live public screenshot. Coverage Tier: route/form rendering. Submit success/admin review is a separate workflow proof.

Tenant Lifecycle & DR admin

The platform decides each site's data-isolation architecture from a single VIP flag: VIP ⇒ database-per-tenant (the site gets its own physical database); non-VIP ⇒ shared-table Row-Level Security (the "D2" model — shared database and schema, isolated by RLS keyed on the site, enforced at runtime via a non-superuser application role). VIP is platform-controlled today.

Platform super-admin — "Sites & DR" (CMS /platform/sites)

Platform super-admins open http://localhost:23000/platform/sites to manage every site from one table. Each row shows the site, VIP star, tier (SCHEMA or DATABASE), base URL, and custom-domain verification state. Per-row actions:

  • Set / Remove VIP — opens a confirm dialog that explains it auto-migrates the architecture: making a site VIP moves it onto its own physical database; removing VIP downgrades it back to the shared schema + RLS. The migration runs a fail-safe pg_dump/restore sequence (provision the target, copy, verify, then drop the source last). Note tenant-demo shows Set VIP while the three DATABASE sites show Remove VIP.
  • Close — DR-safe decommission. It requires typing the site slug to confirm and backs the site up before dropping it. No accidental default-true confirmation.
  • Verify — verifies a pending custom domain (checks the required DNS-TXT record).

The Spawn site and Backup (configure / run / restore-test) actions render disabled — those operations are not available yet.

Platform Sites & DR admin table with 4 seeded sites including a SCHEMA-tier non-VIP site
Evidence Source: live CMS runtime screenshot, hybrid auth (cookie bootstrap), showing the Sites & DR table with 4 real seeded sites — three DATABASE/VIP and one SCHEMA/non-VIP (tenant-demo). Coverage Tier: mocked-GraphQL component tier + this hybrid backend-backed capture. Readiness State: PASS for the admin surface; not a full backend-backed browser e2e.

Tenant admin — "DR & Domain" (CMS /settings/dr)

A site/tenant admin opens http://localhost:23000/settings/dr to manage their own site. Tier and VIP are shown read-only (platform-controlled; to change them, contact the platform). The panel also shows the base URL and the custom-domain DNS-TXT verification state, plus a URL-settings form: choose how the site is reached — a subdomain (slug.platform), a path fallback (platform/slug), or an optional custom domain — and save it.

Note: all four seeded sites currently set their domain to none, so the pending-domain "Verify" affordance is still not seed-exercised; and the per-tenant /settings/dr panel is not captured this pass (see Gaps).

Host Preflight & Deploy Operator Flow New

Backend / CLI surface (no browser). Before pointing the non-VIP read path at any PostgreSQL host, you confirm the host can satisfy the D2 model, then run a single idempotent provisioning script. Evidence here is command transcripts, not screenshots.

1 · Capability preflight — migrate db-doctor

Run against any candidate Postgres to confirm it can run the D2 read path (create a non-superuser app role and have FORCE RLS actually confine it) and whether it has pgvector + Apache AGE for runtime GraphRAG (advisory — their absence does not block D2). db-doctor inspect is read-only; the default mode runs a throwaway role round-trip. Transcript against the governed PG15:

$ migrate db-doctor
ℹ️  [INFO] server-version       PostgreSQL 15.18
✅ [PASS] privileged-role-can-create-app-role
✅ [PASS] app-role-is-non-superuser
✅ [PASS] app-role-non-bypassrls
✅ [PASS] force-rls-confines-app-role
⚠️  [WARN] tls                  NOT using TLS — set sslmode=require for a hosted DB
ℹ️  [INFO] max-connections      max_connections=100
⚠️  [WARN] extension:age        age NOT available — runtime GraphRAG cannot run here
⚠️  [WARN] extension:vector     vector NOT available — runtime GraphRAG cannot run here

✅ HOST OK for the D2 shared-table-RLS read path (advisory WARN items do not block D2).
Evidence Source: real CLI transcript (docs/manual/assets/host-doctor-cli-2026-06-16.txt) against the governed PG15. Coverage Tier: backend-tool-cli / runtime-backed. Readiness State: the host is D2-capable; GraphRAG is advisory-unavailable on this DB (no pgvector/AGE) — exactly the host-selection signal the doctor exists to surface.

2 · Provision — scripts/provision_schema_tier_rls.py

One idempotent command provisions the non-superuser app role, seeds the non-VIP site(s), installs the shared-table RLS, and verifies isolation. Preview the exact plan first with --dry-run (prints commands + a secret-redacted DSN, executes nothing):

$ provision_schema_tier_rls.py --dry-run tenant-demo
D2 shared-table RLS on host=db.example.com … password=*** sslmode=require | app_role=cms_tenant_app app_password=***

--dry-run: the following idempotent steps WOULD run (nothing executed):
  ▶ 1/4 provision non-superuser app role:  migrate provision-app-role
  ▶ 2/4 seed schema-tier site 'tenant-demo': migrate migrate tenant-demo
  ▶ 3/4 install shared-table RLS + grant:    migrate rls-rollout-shared
  ▶ 4/4 verify RLS isolation:                migrate verify-rls tenant-demo

The runtime env contract is documented in .env.schema-tier.example; the routed /mcp public read path turns on only when SCHEMA_TIER_APP_USER is set at backend startup (default-off / reversible). After deploy, a post-deploy /mcp cross-tenant UAT smoke (env MCP_UAT_BASE_URL) confirms isolation against the real URL.

Evidence Source: real CLI transcript (docs/manual/assets/provision-dryrun-cli-2026-06-16.txt). Coverage Tier: backend-tool-cli / runtime-backed locally. Readiness State: the operator flow was runtime-backed on a governed PG15 and has now been applied to live Cloud SQL; public-domain MCP proof remains pending.

Source Manifest

These are the exact inputs used for this manual refresh. RTM.md is traceability context only; spec-local reviews and runtime reports remain proof authority.

SourceHow it was used
.agents/specs/{SPECS,NEXT_STEPS,ISSUE_LOG,RTM}.md2026-06-27 rollup — Cloud Run deployment, live Cloud SQL RLS follow-up, and prior D2/manual evidence. RTM is rollup only, not readiness authority.
.agents/specs/schema-per-tenant-routing-rls/{tasks.md (Slice 8), review.md, adr/ADR-RLS-002-NON-VIP-CANONICAL-D2.md}The D2 (non-VIP) canonical model + the host-preflight / dry-run / UAT-smoke / transaction-pool-safety deliverables documented in the Host Preflight section. RLS is only enforced when the app connects as a non-superuser role.
.agents/specs/tenant-lifecycle-dr-admin/{design-cms-ui.md, requirements.md, tasks.md}DR admin UI shape for the platform Sites & DR table and tenant DR & Domain panel. (No review.md exists for this spec — fell back to the design/requirements/tasks chain, as the guide allows.)
backend/seeds/{platform,naelt,showcase,tenant-demo}-*.jsonCanonical seed/demo/sample data for the 4 seeded sites (the new tenant-demo carries tenancyTier:schema).
docs/manual/assets/*-2026-06-16.{png,txt}9 fresh screenshots + 2 CLI transcripts captured this pass from the running local stack via scripts/capture_manual_2026_06_16.mjs and the migrate CLI.
docs/MANUAL_GENERATION_GUIDE.md + the review guidesManual workflow and source-manifest rules (2026-06-27 note added for Cloud Run/RLS addendum, no new screenshots).
.agents/specs/production-deployment-topology/{review.md,adr/ADR-DEPLOY-003-gcp-cloudrun-cloudsql-cloudflare.md} + infra/terraform/envs/gcp-cloudrun/Production deployment authority: Cloud Run runtime live on run.app, Cloud SQL/Upstash connectivity, declarative Terraform IaC, and live shared-table RLS provisioning/wiring claim boundary.
docs/SHOWCASE_GENERATION_GUIDE.md2026-07-11 generation memo for keeping showcase/docs-hub copy, manual operator notes, and review evidence boundaries separate.
docs/*FEATURE*.mdNo matching files exist in this repo for this pass (manifest fallback to the spec/report chain, as the guide allows).
docs/*SPEC*.mdMatching docs-spec files are historical/secondary context; active spec artifacts and RTM remain the readiness authority for this manual pass.

Gaps And Changes Since Last Check

Compared against the 2026-06-15 manual pass.

Gaps resolved this pass (2026-06-16)

  • Non-VIP / SCHEMA tier now visually exercised: seeded a fourth site tenant-demo on the SCHEMA (non-VIP) tier, so the DR admin table now shows the SCHEMA badge and the Set VIP (vs Remove VIP) distinction with real data, and the non-VIP D2 public site renders at /site/tenant-demo.
  • Host-preflight operator tooling documented: the new migrate db-doctor capability probe (incl. pgvector/AGE detection) and the provision_schema_tier_rls.py --dry-run plan are now shown with real CLI transcripts.
  • Seed command fixed: scripts/ops.py db seed invoked the old single-file go run cmd/migrate/main.go (broken after the migrate package was split into multiple files); corrected to go run ./cmd/migrate so seeding works again.
  • All public routes still 200 against the real backend, now including /site/tenant-demo (6 routes).

Visual gaps remaining

  • Pending-domain "Verify" + /settings/dr panel: all four seeded sites set domain to none, so the custom-domain pending/Verify state is still not seed-exercised, and the per-tenant DR & Domain panel is not captured this pass. Add a site with a pending custom-domain challenge.
  • Backup / spawn disabled: the backup and spawn actions render disabled (not yet built).
  • GraphRAG-capable host not shown: the governed test DB lacks pgvector/AGE, so db-doctor shows them as advisory WARN; a host with both installed is not captured.
  • Hybrid CMS captures + dev overlay: CMS screenshots use auth-API cookie bootstrap (hybrid), not full browser-login; a Next dev "1 Issue" overlay badge appears in CMS captures (dev overlay, not a product defect).
High

Production target

target_id remains undecided; backend host is not provisioned; DNS/TLS/deployment binding is absent for app/API hostnames.

High

Production visual/SEO/CDN

Current screenshots are local runtime captures; custom-domain browser proof, CDN/cache invalidation, crawler breadth, and UAT remain separate despite Cloud Run being live.

Medium

Public-domain D2/MCP proof

The live Cloud SQL RLS layer is provisioned and wired, but custom-domain /mcp cross-tenant proof still waits for the API certificate and at least two non-VIP tenants.

Medium

DR admin proof tier

The tenant-lifecycle DR admin UI is proven at mocked-GraphQL component tier + this hybrid backend-backed capture — not a full backend-backed browser e2e.

Medium

Browser-login breadth

CMS dashboard / page-builder / DR-admin captures use auth-API cookie injection; hybrid evidence, not full browser-login.

Medium

Live page-save / A2UI apply

The page-builder screenshot shows the seeded Pages surface; live page-save/publish and A2UI agent apply are separate workflow proofs not captured here.