diff --git a/.gitignore b/.gitignore index 4da2e13..a9639d6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ terraform.tfvars terraform.tfvars.json tfplan tfplan.* +.vercel diff --git a/AGENTS.md b/AGENTS.md index 5175bc7..707e5a6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -58,6 +58,12 @@ Prefer explicit labels in docs: - If the user intent is clear and the step is ship-safe, continue to commit and push without asking again. - Ask before pushing only for risky/destructive/security/billing posture changes, or when the user explicitly asks to hold. +## Infrastructure and environment rule + +- Prefer infrastructure-as-code or checked-in configuration for infrastructure, CI settings, and environment variable definitions whenever the platform supports it. +- Especially avoid undocumented dashboard-only environment variables; if a secret value must stay in a provider secret store, commit the expected variable name, scope, owner, and rotation/recreation path. +- Manual provider changes are acceptable only as a bootstrap or emergency step, and should be followed by docs, scripts, Terraform, or workflow changes that make the desired state reproducible. + ## Global vs local agent context - Put repo-wide defaults, durable workflow rules, and opinionated project conventions in `AGENTS.md`. diff --git a/apps/web/e2e/public-routes.smoke.spec.ts b/apps/web/e2e/public-routes.smoke.spec.ts index 4ec5133..e45b8b2 100644 --- a/apps/web/e2e/public-routes.smoke.spec.ts +++ b/apps/web/e2e/public-routes.smoke.spec.ts @@ -1,8 +1,10 @@ import { test } from "@playwright/test"; -import { capturedRoutes } from "./public-routes"; +import { capturedRoutes, productionSmokeRoutes } from "./public-routes"; -for (const route of capturedRoutes) { +const routes = process.env.PLAYWRIGHT_BASE_URL ? productionSmokeRoutes : capturedRoutes; + +for (const route of routes) { test(`${route.name} renders`, async ({ page }) => { await page.goto(route.path); await route.expectPage(page); diff --git a/apps/web/e2e/public-routes.ts b/apps/web/e2e/public-routes.ts index 10b428c..3732e17 100644 --- a/apps/web/e2e/public-routes.ts +++ b/apps/web/e2e/public-routes.ts @@ -347,3 +347,9 @@ export const capturedRoutes: CapturedRoute[] = [ expectPage: expectEventPage, }, ]; + +export const productionSmokeRoutes: CapturedRoute[] = capturedRoutes.filter((route) => + ["submit", "sign-in", "privacy-suppression", "event-new-signed-out", "server-status", "deployment"].includes( + route.name, + ), +); diff --git a/docs/deployment/convex-environments.md b/docs/deployment/convex-environments.md index 8154c82..dbb9880 100644 --- a/docs/deployment/convex-environments.md +++ b/docs/deployment/convex-environments.md @@ -12,6 +12,8 @@ VRDex keeps three Convex execution targets separate: Do not commit deploy keys. Store them in GitHub/hosting secret stores and local ignored env files only. +Current recommendation: define environment variable names and target scopes in docs or IaC first, then set secret values through provider secret stores. Manual Convex dashboard edits should be treated as bootstrap/emergency changes and followed by a reproducibility update here or in automation. + - development cloud URL: `https://scrupulous-corgi-247.convex.cloud` - production cloud URL: `https://superb-pig-954.convex.cloud` @@ -27,6 +29,17 @@ Local ignored env names: - `CONVEX_URL_DEV` - `CONVEX_URL_PROD` +## Hosted E2E Helpers + +Hosted mutation-backed Playwright runs use only the shared development/staging target. Do not enable these helpers in production. + +Development/staging Convex env names: + +- `VRDEX_ENABLE_E2E_HELPERS=true` +- `VRDEX_E2E_CONVEX_SECRET`: non-empty sentinel also configured in the hosted app environment + +The browser-facing token stays in the web host and GitHub Actions as `VRDEX_E2E_BROWSER_TOKEN` / `VRDEX_HOSTED_E2E_BROWSER_TOKEN`; it is not needed by Convex. + ## Notes There are two similarly named Convex projects in the account history: `vrdex` and `vrdex-85631`. Current recommendation is to keep the `vrdex` line of deployments and archive/delete the other only after confirming no dashboard, env, or deployment history still depends on it. diff --git a/docs/deployment/vercel-preview.md b/docs/deployment/vercel-preview.md index ffa82d5..566ff50 100644 --- a/docs/deployment/vercel-preview.md +++ b/docs/deployment/vercel-preview.md @@ -19,6 +19,8 @@ Run Vercel CLI commands from the repository root once the project root directory ## Repository secrets +Current recommendation: keep repository Actions variables and secrets reproducible through checked-in workflows/docs first, and provider APIs or CLI scripts where practical. Secret values still belong in GitHub/Vercel/Convex secret stores, but their names, scopes, and recreation path should be documented here. + The PR workflow deploys a Vercel preview only when all three repository secrets exist: - `VERCEL_TOKEN` @@ -43,6 +45,14 @@ Set these in the Vercel project as needed: Do not set `VRDEX_ENABLE_PLAYWRIGHT_FIXTURES` in Vercel. Fixture profiles are for Playwright-only local/CI preview screenshots and must not be exposed from hosted previews. +Hosted dev/staging E2E targets must set these only on the dev/staging environment, not production: + +- `VRDEX_ENABLE_E2E_HELPERS=true` +- `VRDEX_E2E_BROWSER_TOKEN`: same value as the GitHub Actions secret `VRDEX_HOSTED_E2E_BROWSER_TOKEN` +- `VRDEX_E2E_CONVEX_SECRET`: non-empty sentinel matching the Convex deployment secret name + +Production should keep `VRDEX_ENABLE_E2E_HELPERS=false` or unset, and should not set `VRDEX_ALLOW_PRODUCTION_E2E_HELPERS` unless a human explicitly approves a temporary incident/debug window. + Preview deployment protection must allow unauthenticated reads if the PR preview is meant to be reviewed outside the Vercel dashboard. ## Validation diff --git a/docs/planning/engineering-strategy.md b/docs/planning/engineering-strategy.md index a7d6260..d60669d 100644 --- a/docs/planning/engineering-strategy.md +++ b/docs/planning/engineering-strategy.md @@ -124,6 +124,9 @@ Infra direction: - Terraform and/or AWS CDK are both acceptable directions - choose one primary IaC path before implementation gets too far +- prefer infrastructure-as-code or checked-in config for infrastructure, CI settings, and environment variable definitions whenever the platform supports it +- for secrets that must remain in provider secret stores, commit the expected variable name, environment scope, owning service, and rotation/recreation path instead of relying on dashboard-only tribal knowledge +- treat manual dashboard changes as bootstrap or emergency operations that need a follow-up reproducibility artifact ## Follow-on integration ideas diff --git a/docs/testing/playwright-visual-preview.md b/docs/testing/playwright-visual-preview.md index 9508c1b..cfbb6dd 100644 --- a/docs/testing/playwright-visual-preview.md +++ b/docs/testing/playwright-visual-preview.md @@ -12,7 +12,7 @@ See `docs/testing/playwright-image-diffing.md` for the committed-baseline image - Update public route screenshot baselines: `pnpm test:e2e:snapshots:update` - Reuse already-running local services: set `PLAYWRIGHT_REUSE_SERVER=true` and `PLAYWRIGHT_REUSE_CONVEX=true` - Run the mutation-backed flow against a hosted dev/staging target: set `PLAYWRIGHT_BASE_URL` and `VRDEX_E2E_BROWSER_TOKEN`, then run `pnpm test:e2e:hosted` -- Run read-only smoke against a hosted production target: set `PLAYWRIGHT_BASE_URL`, then run `pnpm test:e2e:hosted:smoke` +- Run read-only smoke against a hosted production target: set `PLAYWRIGHT_BASE_URL`, then run `pnpm test:e2e:hosted:smoke`. Hosted smoke covers production-safe public routes only; fixture-backed profile/search expectations stay local because Vercel must not expose Playwright fixtures. PowerShell data-flow run with video: