Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
deno-version: 2.9.6
cache: true

- name: Documentation
run: npm run docs:check

- name: Lint
run: npm run lint

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ humanizer.md
!README.md
!AGENTS.md
!**/README.md
!docs/pricing-snapshots/*.md
!docs/**/*.md
!plans/**/*.md
.vercel
.env*
Expand Down
16 changes: 14 additions & 2 deletions .superset/db-gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,23 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/lib/lock.sh"

DB_LOCK="$HOME/.superset/debatelab-db.lock"
stack_started=false

cleanup() {
local status=$?
trap - EXIT

if [ "$stack_started" = true ] && ! supabase stop --no-backup; then
[ "$status" -ne 0 ] || status=1
fi
lock_release "$DB_LOCK"
exit "$status"
}

echo "Waiting for exclusive access to the local Supabase stack..."
lock_acquire "$DB_LOCK" 600
trap 'lock_release "$DB_LOCK"' EXIT
trap cleanup EXIT

supabase db start
stack_started=true
npm run test:db
supabase stop --no-backup
92 changes: 56 additions & 36 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
# DebateLab agent guide

## Runtime and install
## Start here

- Use Node 22 and npm. The lockfile is `package-lock.json`.
- Start a clean checkout with `npm ci`.
- The development server normally uses port 8080. Automated browser verification owns `127.0.0.1:18080` and never reuses an existing server.
- Local Supabase development uses port 54321. Do not point staging checks at the production project.
- The repository is DebateLab. Never infer its directory from an old repository name.
- Run `git rev-parse --show-toplevel` and stay inside that returned worktree. On Adam's VPS, `/home/codex/Projects/DebateLab` is the primary checkout, but Superset and other agents use linked worktrees elsewhere.
- Before editing, inspect the branch and working tree. Preserve unrelated changes.
- Use Node 22, npm, and the committed `package-lock.json`. Install with `npm ci`.

## Parallel Superset workspaces

Each Superset workspace is a separate git worktree. `.superset/config.json` runs `npm ci` on create and `./.superset/dev.sh` for the Run button; `dev.sh` allocates a free port for this workspace from a shared `~/.superset/debatelab-ports/allocations.json` file (range 8080-8179) so several workspaces' dev servers never collide on the hardcoded `:8080` in `vite.config.ts`, and `./.superset/teardown.sh` releases that allocation on delete.
Each Superset workspace is a separate git worktree. `.superset/config.json` runs `npm ci` when it creates a workspace and starts the app through `./.superset/dev.sh`. That script allocates a port from `8080-8179` in `~/.superset/debatelab-ports/allocations.json`; `./.superset/teardown.sh` releases it.

The local Supabase stack is not parallel-safe: `supabase/config.toml` sets no `project_id` or custom port block, so `supabase db start` always binds the CLI's default ports and container names. Run the DB gate through `./.superset/db-gate.sh` rather than calling `supabase db start` / `npm run test:db` directly whenever more than one workspace might be active - it takes an exclusive lock (`~/.superset/debatelab-db.lock`, up to 10 minutes) before starting the stack and stops it afterward.
The local Supabase stack still uses shared default ports and container names. In Superset, run the database gate through `./.superset/db-gate.sh`. It holds `~/.superset/debatelab-db.lock` while the stack runs and stops the stack afterward.

## Verification ladder
## System map

Run the smallest relevant reproduction first, then the complete gate before a PR:
- `src/` is the React 18 and TypeScript client. Routes and lazy page entry points live in `src/App.tsx`.
- `src/lib/stores/` owns browser state and Supabase hydration. `src/lib/ai/client.ts` is the client AI boundary.
- `supabase/functions/` contains Deno Edge Functions. Shared auth, CORS, research, pricing, and validation code lives in `supabase/functions/_shared/`.
- `supabase/migrations/` is the database history and schema source of truth.
- `.github/workflows/` owns CI behavior. `Dockerfile` and `nginx.conf` own production packaging.

## Normal ports

- Vite development server: `8080` by default outside Superset; Superset allocates `8080-8179` per worktree.
- Automated browser server: isolated `127.0.0.1:18080`, never an existing dev server.
- Local Supabase: CLI defaults beginning at `54321`. The stack is not parallel-safe across worktrees.

## Focused verification

Run the smallest relevant check first:

```bash
npm run docs:check
npm run lint
npm run typecheck
npm test -- --coverage
Expand All @@ -26,51 +41,56 @@ npm run build
CI=1 PORT=18080 PLAYWRIGHT_BASE_URL=http://127.0.0.1:18080 npm run test:e2e
```

`npm run verify` runs that full credential-free ladder. It starts an isolated Vite server and executes the desktop and mobile Chromium projects. Browser failures stay in ignored `test-results/` and `playwright-report/` directories. CI uploads those directories for seven days when the browser step fails.
`npm run verify` runs the complete credential-free ladder, including desktop and mobile Chromium against a fresh Vite server.

Database migrations and pgTAP policies have a separate Docker-backed gate. Supabase CLI 2.115 or newer is required so `CREATE INDEX CONCURRENTLY` runs outside the migration batch:
Database migrations and pgTAP policies use a separate Docker-backed gate. In Superset, use the locking wrapper:

```bash
./.superset/db-gate.sh
```

Outside Superset, run the underlying commands directly:

```bash
supabase db start
npm run test:db
supabase stop --no-backup
```

WebKit is an additional release smoke gate:
Supabase CLI 2.115 or newer is required. Do not run this gate concurrently from multiple worktrees. If this task starts the stack, stop it before finishing, including after failures when safe.

WebKit is a separate release smoke test:

```bash
npx playwright install --with-deps webkit
CI=1 PORT=18080 PLAYWRIGHT_BASE_URL=http://127.0.0.1:18080 npm run test:e2e:webkit
```

## Protected staging verification

The staging suite uses a real staging user and backend. It never uses a service-role key. Supply all three protected values together:
## Hard boundaries

- `STAGING_SUPABASE_URL`
- `STAGING_SUPABASE_ANON_KEY`
- `STAGING_DEMO_CODE`
- Start bug fixes with a failing test or repeatable browser reproduction when a suitable test layer exists.
- Stub sessions prove rendering only. Do not call them authenticated backend proof.
- Never put staging or production credentials in jobs, logs, traces, screenshots, commits, comments, or docs.
- Never substitute the production project when local or staging access is unavailable.
- Production deployments, data changes, migrations, Edge Function deployments, and secret changes require explicit authorization.
- A local pass proves only the local commit. Check the current-head GitHub run before calling CI green.
- A skipped staging job is not a pass. Report exactly which browser and staging checks ran.
- Do not weaken tests, RLS, authentication, branch protection, or required checks to make a run pass.

Then run:

```bash
CI=1 PORT=18080 PLAYWRIGHT_BASE_URL=http://127.0.0.1:18080 \
STAGING_SUPABASE_URL=<staging-url> \
STAGING_SUPABASE_ANON_KEY=<staging-anon-key> \
STAGING_DEMO_CODE=<dedicated-test-code> \
npm run test:e2e:staging
```
## Protected staging

The suite rejects the production project, checks that the anon key belongs to the supplied staging URL, authenticates through `demo-code-login`, proves session reload, writes and reads back one unique source, makes one bounded AI request, and deletes only that source. It saves temporary auth state under `test-results/.auth/`. Staging disables traces, screenshots, and video, and its workflow uploads no browser artifacts.
Staging E2E requires all three values together: `STAGING_SUPABASE_URL`, `STAGING_SUPABASE_ANON_KEY`, and `STAGING_DEMO_CODE`. The suite rejects the production project, disables browser captures, writes only uniquely named test data, and deletes only that data. Read [docs/testing.md](docs/testing.md) before running it and [docs/operations.md](docs/operations.md) before changing staging configuration.

GitHub runs staging only for `main` push or a main-branch manual dispatch, through the protected `staging` environment, after the credential-free build job passes. Set the repository variable `STAGING_E2E_ENABLED=true` only after the protected environment has the three secrets, current staging functions, schema, provider secret, dedicated unlimited-use test code, and a main-only deployment rule. A skipped staging job is not a pass.
## Documentation router

The staging origin `http://127.0.0.1:18080` must be deployed in the CORS allowlists for `demo-code-login`, `ai`, `ai-stream`, `admin`, and `announcements` before enabling the gate. Do not deploy those functions or apply migrations to an unverified project.
Open only what the task needs:

## Working rules
- [docs/architecture.md](docs/architecture.md): runtime boundaries, request flows, state, auth, billing, observability, PWA, and iOS.
- [docs/development.md](docs/development.md): setup, environment behavior, worktrees, and common workflows.
- [docs/testing.md](docs/testing.md): what each gate proves, browser matrices, staging, artifacts, and cleanup.
- [docs/operations.md](docs/operations.md): CI, Docker, Nginx, staging, deployment, rollback, and live verification.
- [supabase/README.md](supabase/README.md): Edge Functions, shared backend modules, migrations, auth boundaries, and required secret names.
- [plans/README.md](plans/README.md): active plans. Revalidate every plan against current source before executing it.
- [docs/README.md](docs/README.md): complete documentation index and freshness labels.

- Start bug fixes with a failing test or a repeatable browser reproduction when the repository has a suitable test layer.
- Keep stub sessions limited to render checks. Do not describe them as authenticated backend proof.
- Never put staging or production secrets in PR jobs, logs, traces, screenshots, commits, or comments.
- A local pass proves only the local commit. Inspect the current-head GitHub run before calling CI green. Account billing or spending-limit failures are external exceptions, not passing checks.
- State exactly which browser and staging checks ran. If credentials, provider access, or staging synchronization are missing, stop that gate and report it instead of substituting production or mocks.
When commands, ports, workflows, or runtime boundaries change, update the owning focused document in the same change. Do not copy large source trees, schemas, dependency lists, or model catalogs into documentation.
Loading
Loading