# Staging Environment Plan

## Goal

Create a staging environment that mirrors production closely enough to validate migrations, tenant seed runs, localization changes, email behavior, and AI workflow changes before they touch the production database.

Staging should use separate infrastructure from production. It should never share the production Postgres database, Auth.js secret, Vercel deployment, email sender, Inngest environment, or AI observability data.

## Current Production-Only Workaround

Until staging exists, tenant-scoped production seed work should only be done for a deliberately selected tenant.

For the Ask QIEN operators tenant, the production seed command should target the operators config file:

```bash
cd /workspace
TENANT_CONFIG_PATH=./tenant.config.prod.operators.yaml \
DATABASE_URL="<production pooled database url>" \
SEED_DEMO_DATA=true \
pnpm db:seed
```

This targets tenant slug `askquien-ops` from `tenant.config.prod.operators.yaml`.

Use this only if you intentionally want the operators tenant to receive the demo KB seed/backfill behavior. Do not run this command with `tenant.config.prod.yaml` unless you intentionally want to seed demo data into the Confluent-CR tenant.

For real customer tenants, use a purpose-built tenant-scoped translation backfill script instead of `SEED_DEMO_DATA=true pnpm db:seed`.

## Staging Shape

Staging should have:

- A separate Vercel project or a separate Vercel environment with its own URL.
- A separate managed Postgres database.
- A separate Auth.js secret.
- Separate Inngest app/environment and signing key.
- Separate email provider credentials, ideally a sandbox/test mailbox.
- Separate AI provider credentials or a constrained staging key.
- Its own tenant config file, for example `tenant.config.staging.operators.yaml`.
- Its own GitHub Actions deploy path or a manual deploy workflow.

## User-Owned Tasks

These require account access or billing/admin permissions.

1. Create a staging Postgres database.
   - Recommended: a separate Neon project or separate Neon branch with an independent connection string.
   - Capture both pooled and non-pooled URLs if the provider exposes both.

2. Create a staging Vercel project or staging environment.
   - Set the staging branch or manual deployment target.
   - Add a distinct staging domain if desired.

3. Add staging environment variables in Vercel.
   - `DATABASE_URL`
   - `POSTGRES_URL_NON_POOLING`
   - `AUTH_SECRET`
   - `AUTH_URL` or equivalent deployment URL setting if needed
   - `EMAIL_SERVER_HOST`
   - `EMAIL_SERVER_PORT`
   - `EMAIL_SERVER_USER`
   - `EMAIL_SERVER_PASSWORD`
   - `EMAIL_FROM`
   - AI provider keys
   - Inngest signing/event keys

4. Create or configure a staging Inngest environment.
   - Use separate signing and event keys.
   - Confirm staging events cannot trigger production functions.

5. Create a staging email setup.
   - Prefer sandbox mode, a test SMTP mailbox, or a provider test domain.
   - Confirm staging emails cannot reach real customer users accidentally.

6. Decide who may access staging.
   - Add only operator/test emails to staging tenant configs.
   - Do not seed customer production users unless intentionally testing their flow.

7. Add GitHub secrets for staging deploys.
   - For example: `STAGING_POSTGRES_URL_NON_POOLING`, `STAGING_VERCEL_DEPLOY_HOOK`, and any staging-only deploy credentials.

## Code-Owned Tasks

These can be implemented in the repository.

1. Add staging tenant config files.
   - Example: `tenant.config.staging.operators.yaml`.
   - Use only test/operator emails.
   - Include representative projects and intake options.

2. Add a staging deploy workflow.
   - Create a GitHub Actions workflow that runs migrations against staging using the staging non-pooled database URL.
   - Trigger the staging Vercel deploy hook after migrations pass.
   - Keep production deploy workflow unchanged.

3. Add staging seed documentation and commands.
   - Document how to run:

```bash
TENANT_CONFIG_PATH=./tenant.config.staging.operators.yaml \
DATABASE_URL="<staging pooled database url>" \
SEED_DEMO_DATA=true \
pnpm db:seed
```

4. Add a tenant-scoped translation backfill script.
   - Inputs: `TENANT_SLUG`, `DRY_RUN=true|false`, optional content scopes.
   - It should never run across all tenants by default.
   - It should report counts before writing.
   - It should skip existing fresh translations.
   - It should write source hashes and review statuses consistently.

5. Add a staging smoke-check script.
   - Verify migrations applied.
   - Verify the staging tenant exists.
   - Verify operator users exist.
   - Verify login/magic-link setup is reachable.
   - Verify no production tenant slugs are present unless explicitly allowed.

6. Add a staging runbook.
   - Migration command.
   - Seed command.
   - Smoke-check command.
   - Rollback/restore notes.
   - Known limitations.

7. Add guardrails to dangerous scripts.
   - Require `TENANT_SLUG` for production backfills.
   - Require `CONFIRM_PRODUCTION_TENANT_SLUG=<slug>` when `NODE_ENV=production` or when the DB URL points at production.
   - Default to dry-run mode.

## Recommended Order

1. User creates staging database and Vercel/Inngest/email environments.
2. Code adds staging tenant config and staging deployment workflow.
3. Run migrations against staging.
4. Seed staging operators tenant with demo data.
5. Run smoke checks and core Playwright flows against staging.
6. Add tenant-scoped production backfill script.
7. Test the backfill script in staging with `DRY_RUN=true`, then `DRY_RUN=false`.
8. Run the production backfill only for the intended tenant slug.

## Done Criteria

- Staging deploy has its own URL.
- Staging uses a separate database from production.
- Staging migrations run automatically before staging deploy.
- Staging seed can be run without touching production.
- Staging email cannot accidentally notify real customer users.
- A smoke-check command confirms tenant, user, and schema health.
- Production tenant backfills require an explicit tenant slug and confirmation.
- The operators tenant can be safely used for production-only validation when staging is unavailable.
