# Phase 1a — Rename Per-Window Tables Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Rename the existing per-window tables (`participants`, `participant_expected_values`, `participant_qualifying_totals`, `participant_results`, `participant_surface_elos`) to `season_`-prefixed names, and rename `event_results.participant_id` to `season_participant_id`. Rename `app/models/participant.ts` to `app/models/season-participant.ts`. Ship a fully green build with zero behavior changes. **Architecture:** Pure mechanical rename across ~100 files. No new tables, no new columns, no logic changes. This phase must land cleanly before Phase 1b can create the new canonical `participants` table (which reuses the freed name). **Tech Stack:** Drizzle ORM, PostgreSQL, React Router 7, TypeScript, Vitest. **Reference:** `docs/superpowers/specs/2026-05-01-canonical-tournament-layer-design.md` — Phase 1a section. **Rename mapping (authoritative):** | Old (table / column / symbol) | New | |---|---| | table `participants` | `season_participants` | | table `participant_expected_values` | `season_participant_expected_values` | | table `participant_qualifying_totals` | `season_participant_qualifying_totals` | | table `participant_results` | `season_participant_results` | | table `participant_surface_elos` | `season_participant_surface_elos` | | column `event_results.participant_id` | `event_results.season_participant_id` | | Drizzle export `participants` | `seasonParticipants` | | Drizzle export `participantExpectedValues` | `seasonParticipantExpectedValues` | | Drizzle export `participantQualifyingTotals` | `seasonParticipantQualifyingTotals` | | Drizzle export `participantResults` | `seasonParticipantResults` | | Drizzle export `participantSurfaceElos` | `seasonParticipantSurfaceElos` | | Drizzle export `participantsRelations` | `seasonParticipantsRelations` | | Drizzle export `participantExpectedValuesRelations` | `seasonParticipantExpectedValuesRelations` | | Drizzle export `participantQualifyingTotalsRelations` | `seasonParticipantQualifyingTotalsRelations` | | Drizzle export `participantResultsRelations` | `seasonParticipantResultsRelations` | | Drizzle export `participantSurfaceElosRelations` | `seasonParticipantSurfaceElosRelations` | | File `app/models/participant.ts` | `app/models/season-participant.ts` | | Relation field `eventResults.participant` | `eventResults.seasonParticipant` | **Files affected (counts from pre-flight audit):** - `database/schema.ts` (1 — critical) - `app/models/index.ts` (1) - `app/models/` — 11 model files + 2 test files - `app/routes/` — ~20 route files - `app/services/` — probability-updater + 19 simulator files - `app/utils/` — `sports-data-sync.server.ts` - `server/` — `socket.ts`, test files - `cypress/` — verify none reference these symbols by name **Note on granularity:** Because this is a mechanical rename, tasks are batched by scope rather than broken into micro-TDD steps. Each task ends with typecheck + test run as the verification step. --- ### Task 0: Create baseline snapshot (one-time, before any code changes) **Why:** Phase 2 verification depends on proving QP totals didn't drift. Capture now. **Files:** - Create: `scripts/capture-baseline.ts` - [ ] **Step 1: Write the capture script** ```typescript // scripts/capture-baseline.ts import { db } from "~/db/context"; import { sql } from "drizzle-orm"; import { writeFileSync, mkdirSync } from "fs"; import { join } from "path"; const OUT_DIR = join(process.cwd(), "test-fixtures", "baselines"); async function main() { mkdirSync(OUT_DIR, { recursive: true }); const qpTotals = await db.execute(sql` SELECT pqt.*, ss.id as sports_season_id, s.name as sport_name FROM participant_qualifying_totals pqt JOIN sports_seasons ss ON ss.id = pqt.sports_season_id JOIN sports s ON s.id = ss.sport_id WHERE ss.scoring_pattern = 'qualifying_points' ORDER BY ss.id, pqt.participant_id `); const eventResults = await db.execute(sql` SELECT er.*, se.sports_season_id FROM event_results er JOIN scoring_events se ON se.id = er.scoring_event_id JOIN sports_seasons ss ON ss.id = se.sports_season_id WHERE ss.scoring_pattern = 'qualifying_points' AND er.qualifying_points_awarded IS NOT NULL ORDER BY se.sports_season_id, er.scoring_event_id, er.participant_id `); const surfaceElos = await db.execute(sql` SELECT pse.*, ss.sport_id FROM participant_surface_elos pse JOIN sports_seasons ss ON ss.id = pse.sports_season_id ORDER BY ss.id, pse.participant_id `); writeFileSync(join(OUT_DIR, "qp-totals-pre-phase1.json"), JSON.stringify(qpTotals.rows, null, 2)); writeFileSync(join(OUT_DIR, "event-results-pre-phase1.json"), JSON.stringify(eventResults.rows, null, 2)); writeFileSync(join(OUT_DIR, "surface-elos-pre-phase1.json"), JSON.stringify(surfaceElos.rows, null, 2)); console.log(`Wrote baselines: ${qpTotals.rows.length} QP totals, ${eventResults.rows.length} event results, ${surfaceElos.rows.length} surface elo rows.`); } main().then(() => process.exit(0)).catch(e => { console.error(e); process.exit(1); }); ``` - [ ] **Step 2: Run against production data read replica (or local dev DB loaded from `npm run db:sync-prod`)** Run: `npx tsx scripts/capture-baseline.ts` Expected: three JSON files created under `test-fixtures/baselines/`; non-zero row counts for QP totals and surface elos; Masters 2026 results visible in `event-results-pre-phase1.json`. - [ ] **Step 3: Also capture Monte Carlo simulator output baselines** Extend `scripts/capture-baseline.ts` to, for each in-flight qualifying-points `sports_seasons` row, run the matching simulator with a **fixed random seed** and save its output. This JSON is what Phase 3 Task 6's tennis-simulator regression test reads. Pattern: ```typescript // Pseudocode appended to scripts/capture-baseline.ts main(): import { getSimulator } from "~/services/simulations/registry"; // ... for each ss in qualifying-points seasons: const sim = getSimulator(ss.simulatorType!); const output = await sim.run(ss.id, { seed: 42, iterations: 10000 }); writeFileSync(join(OUT_DIR, `sim-output-${ss.simulatorType}-${ss.id}.json`), JSON.stringify(output, null, 2)); ``` Check the existing `Simulator` interface in `app/services/simulations/types.ts` for the actual method signature; adapt accordingly. If a simulator does not currently accept a `seed`, add one as a non-breaking optional param in this task — it's needed for reproducible baselines. - [ ] **Step 4: Commit the baseline fixtures** ```bash git add scripts/capture-baseline.ts test-fixtures/baselines/ git commit -m "chore: capture pre-rename baselines for qualifying-points data and simulator output" ``` --- ### Task 1: Rename schema exports and table names in `database/schema.ts` **Why first:** Schema is the source of truth; the whole codebase compiles against its exports. Easiest to do in one sweep. **Files:** - Modify: `database/schema.ts` - [ ] **Step 1: Rename table definitions and string names** Apply these replacements in `database/schema.ts`. The safest order: string names first (they're unambiguous), then export names (which must be done with care to avoid breaking references in relations). Table rename block 1 — `participants` (~ lines 376-390): ```typescript // OLD: export const participants = pgTable("participants", { // ... }); // NEW: export const seasonParticipants = pgTable("season_participants", { // ... }); ``` Table rename block 2 — `participant_expected_values` (~ lines 642-672): ```typescript // OLD: export const participantExpectedValues = pgTable("participant_expected_values", { ... }); // NEW: export const seasonParticipantExpectedValues = pgTable("season_participant_expected_values", { ... }); ``` Table rename block 3 — `participant_qualifying_totals` (~ lines 537-550): ```typescript // OLD: export const participantQualifyingTotals = pgTable("participant_qualifying_totals", { ... }); // NEW: export const seasonParticipantQualifyingTotals = pgTable("season_participant_qualifying_totals", { ... }); ``` Table rename block 4 — `participant_results` (~ lines 414-428): ```typescript // OLD: export const participantResults = pgTable("participant_results", { ... }); // NEW: export const seasonParticipantResults = pgTable("season_participant_results", { ... }); ``` Table rename block 5 — `participant_surface_elos` (~ lines 1219-1235): ```typescript // OLD: export const participantSurfaceElos = pgTable("participant_surface_elos", { ... }); // NEW: export const seasonParticipantSurfaceElos = pgTable("season_participant_surface_elos", { ... }); ``` Column rename in `eventResults` table (~ lines 458-476): ```typescript // OLD: participantId: uuid("participant_id").notNull().references(() => participants.id, { onDelete: "cascade" }), // NEW: seasonParticipantId: uuid("season_participant_id").notNull().references(() => seasonParticipants.id, { onDelete: "cascade" }), ``` - [ ] **Step 2: Rename relations exports and their field references** In `database/schema.ts`, update each `*Relations` export and every `one()/many()` call that referenced the renamed tables. Replacements needed: - `participantsRelations` → `seasonParticipantsRelations` - `participantExpectedValuesRelations` → `seasonParticipantExpectedValuesRelations` - `participantQualifyingTotalsRelations` → `seasonParticipantQualifyingTotalsRelations` - `participantResultsRelations` → `seasonParticipantResultsRelations` - `participantSurfaceElosRelations` → `seasonParticipantSurfaceElosRelations` Within `eventResultsRelations` (~ lines 952-961): ```typescript // OLD: participant: one(participants, { fields: [eventResults.participantId], references: [participants.id], }), // NEW: seasonParticipant: one(seasonParticipants, { fields: [eventResults.seasonParticipantId], references: [seasonParticipants.id], }), ``` Update every other relation `one()`/`many()` that referenced `participants`, `participantResults`, `participantQualifyingTotals`, `participantSurfaceElos`, or `participantExpectedValues` to use the new table export names. - [ ] **Step 3: Verify schema parses** Run: `npm run typecheck` Expected: Will fail with many errors in model/route files that still import old names. That's fine for now — schema.ts itself compiling is the win. Confirm the errors are only in files outside `database/schema.ts`. - [ ] **Step 4: Commit schema rename alone** ```bash git add database/schema.ts git commit -m "refactor(schema): rename per-window tables to season_* prefix Renames participants, participant_expected_values, participant_qualifying_totals, participant_results, participant_surface_elos to season_* prefixed names. Renames event_results.participant_id to season_participant_id. Phase 1a of canonical tournament layer migration." ``` --- ### Task 2: Generate and hand-edit the Drizzle rename migration **Why:** `drizzle-kit generate` treats renames as drop+create by default; this loses data. For safe renames, we generate with `--custom` and hand-edit, OR use `drizzle-kit generate` and answer its interactive rename prompts. Using custom is safer here because multiple renames in one migration can confuse the prompt sequence. **Files:** - Create: `drizzle/0087_rename_per_window_tables.sql` (filename assigned by drizzle-kit; adjust to whatever it generates) - Create: `drizzle/meta/0087_snapshot.json` (generated) - [ ] **Step 1: Generate a custom migration scaffold** Run: `npm run db:generate -- --custom --name=rename_per_window_tables` Expected: Creates a new empty `.sql` file in `drizzle/` and a paired snapshot in `drizzle/meta/`. - [ ] **Step 2: Write the rename SQL manually** Open the new `drizzle/XXXX_rename_per_window_tables.sql` and replace its contents with: ```sql -- Rename per-window tables to season_* prefix -- Part of canonical tournament layer migration (Phase 1a) -- Drop the FK on event_results.participant_id before renaming target ALTER TABLE "event_results" DROP CONSTRAINT IF EXISTS "event_results_participant_id_participants_id_fk"; -- Rename tables ALTER TABLE "participants" RENAME TO "season_participants"; ALTER TABLE "participant_expected_values" RENAME TO "season_participant_expected_values"; ALTER TABLE "participant_qualifying_totals" RENAME TO "season_participant_qualifying_totals"; ALTER TABLE "participant_results" RENAME TO "season_participant_results"; ALTER TABLE "participant_surface_elos" RENAME TO "season_participant_surface_elos"; -- Rename column on event_results ALTER TABLE "event_results" RENAME COLUMN "participant_id" TO "season_participant_id"; -- Re-add the FK against the renamed table ALTER TABLE "event_results" ADD CONSTRAINT "event_results_season_participant_id_season_participants_id_fk" FOREIGN KEY ("season_participant_id") REFERENCES "season_participants"("id") ON DELETE cascade ON UPDATE no action; -- Rename any unique indexes that embed the old table names ALTER INDEX IF EXISTS "participant_surface_elos_unique" RENAME TO "season_participant_surface_elos_unique"; -- (Add further ALTER INDEX ... RENAME TO for any other indexes whose names embed old table names. -- Run `\d ` in psql against a fresh migrate to confirm nothing lingers.) ``` **Note:** FK and index constraint names are auto-generated based on table names. After the `ALTER TABLE ... RENAME TO`, Postgres does NOT auto-rename the FK constraint on `event_results` pointing at the old name — we drop and re-create explicitly above. Run the migration against a scratch DB and inspect `\d event_results` and `\d season_participants` to catch any leftover naming. - [ ] **Step 3: Verify snapshot is consistent** Run: `npm run db:generate` Expected: "No schema changes" — confirms the snapshot chain is valid. If drizzle-kit wants to generate another migration, stop and diagnose; the snapshot is out of sync with the custom SQL. - [ ] **Step 4: Run migration on local DB** Run: `npm run db:migrate` Expected: New migration applied; existing data intact (verify row counts match pre-rename via `psql ... -c "SELECT count(*) FROM season_participants;"` = prior `SELECT count(*) FROM participants;`). - [ ] **Step 5: Commit migration** ```bash git add drizzle/ git commit -m "migration: rename per-window tables to season_* prefix" ``` --- ### Task 3: Rename and update `app/models/participant.ts` → `app/models/season-participant.ts` **Files:** - Rename: `app/models/participant.ts` → `app/models/season-participant.ts` - Modify: `app/models/index.ts` - [ ] **Step 1: Git-rename the file** ```bash git mv app/models/participant.ts app/models/season-participant.ts ``` - [ ] **Step 2: Update the file's internal symbol references** Open `app/models/season-participant.ts`. Replace all references to renamed Drizzle exports: - `participants` → `seasonParticipants` - `participantResults` → `seasonParticipantResults` (if referenced) - `participantQualifyingTotals` → `seasonParticipantQualifyingTotals` (if referenced) Update any exported function names that embed "participant" that logically should now be "seasonParticipant" — but **only if they are window-scoped**. E.g., a function `findParticipantsForSeason` likely stays named as-is since callers in routes don't distinguish; a function `createParticipant(sportsSeasonId, data)` likely stays named. Rename only if the name would be genuinely misleading. **Default: keep exported function names to minimize route-layer churn.** - [ ] **Step 3: Update `app/models/index.ts`** Change: ```typescript export * from "./participant"; ``` To: ```typescript export * from "./season-participant"; ``` - [ ] **Step 4: Verify file compiles in isolation** Run: `npx tsc --noEmit app/models/season-participant.ts` (may still reference other unrenamed files — acceptable as long as the error is about imports, not the rename itself.) - [ ] **Step 5: Commit** ```bash git add app/models/season-participant.ts app/models/index.ts git commit -m "refactor: rename participant.ts model file to season-participant.ts" ``` --- ### Task 4: Update all model files referencing renamed schema exports **Files (update each to use new export names):** - `app/models/draft-pick.ts` - `app/models/draft-utils.ts` - `app/models/event-result.ts` - `app/models/group-stage-match.ts` - `app/models/participant-result.ts` - `app/models/qualifying-points.ts` - `app/models/scoring-calculator.ts` - `app/models/scoring-event.ts` - `app/models/sports-season.ts` - `app/models/surface-elo.ts` - `app/models/team-score-events.ts` - [ ] **Step 1: In each file, replace imports and references** Replacement map for imports and schema symbol references throughout the file body: | Old symbol | New symbol | |---|---| | `participants` (Drizzle table) | `seasonParticipants` | | `participantExpectedValues` | `seasonParticipantExpectedValues` | | `participantQualifyingTotals` | `seasonParticipantQualifyingTotals` | | `participantResults` | `seasonParticipantResults` | | `participantSurfaceElos` | `seasonParticipantSurfaceElos` | | `eventResults.participantId` | `eventResults.seasonParticipantId` | | Relation access `.participant` on an `eventResults` row | `.seasonParticipant` | **Keep unchanged:** - Local variable names like `participant`, `participants`, `participantId` — these are runtime identifiers used by business logic; renaming them would cascade into hundreds of lines for zero benefit. Only the *schema symbol* and *Drizzle relation field* change. - Function signatures and param names. - JSDoc. **Example `draft-pick.ts` update:** ```typescript // BEFORE import { participants, draftPicks } from "database/schema"; const [p] = await db.select().from(participants).where(eq(participants.id, participantId)); // AFTER import { seasonParticipants, draftPicks } from "database/schema"; const [p] = await db.select().from(seasonParticipants).where(eq(seasonParticipants.id, participantId)); // ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ schema symbol only // `participantId` param name stays. ``` - [ ] **Step 2: Run typecheck after each batch of 3 files** Run: `npm run typecheck 2>&1 | head -40` Expected: Errors shrink with each file updated. Stop and diagnose if error count grows. - [ ] **Step 3: Update model test files** Files: - `app/models/__tests__/sports-season.clone.test.ts` - `app/models/__tests__/auto-pick.test.ts` - `app/models/__tests__/executeAutoPick.timer.test.ts` Same replacement rules. Run affected tests after update: Run: `npm run test:run -- app/models/__tests__` Expected: PASS. - [ ] **Step 4: Commit** ```bash git add app/models/ git commit -m "refactor(models): update model layer to use renamed schema exports" ``` --- ### Task 5: Update all route files referencing renamed schema exports **Files:** - `app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts` - `app/routes/admin.sports-seasons.$id.events.$eventId.server.ts` - `app/routes/admin.sports-seasons.$id.events.$eventId.cs2-setup.tsx` - `app/routes/admin.sports-seasons.$id.expected-values.server.ts` - `app/routes/admin.sports-seasons.$id.surface-elo.tsx` - `app/routes/admin.sports-seasons.$id.simulate.tsx` - `app/routes/admin.sports-seasons.$id.standings.tsx` - `app/routes/admin.sports-seasons.$id.elo-ratings.tsx` - `app/routes/admin.sports-seasons.$id.tsx` - `app/routes/admin.sports-seasons.$id.golf-skills.tsx` - `app/routes/admin.sports-seasons.$id.regular-standings.tsx` - `app/routes/admin.sports-seasons.$id.futures-odds.tsx` - `app/routes/admin.data-sync.tsx` - `app/routes/api/draft.force-manual-pick.ts` - `app/routes/api/draft.make-pick.ts` - `app/routes/api/draft.replace-pick.ts` - `app/routes/api/seasons.$seasonId.draft.ts` - `app/routes/leagues/$leagueId.draft-board.$seasonId.tsx` - `app/routes/leagues/$leagueId.draft.$seasonId.tsx` - `app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts` - [ ] **Step 1: Apply replacement rules (same map as Task 4) to each file** Use find-and-replace per-file. After every ~5 files, run: Run: `npm run typecheck 2>&1 | wc -l` (Track error count; should decrease monotonically.) - [ ] **Step 2: Verify route tests still pass** Run: `npm run test:run -- app/routes` Expected: PASS (or same-as-before count; no new failures). - [ ] **Step 3: Commit** ```bash git add app/routes/ git commit -m "refactor(routes): update route layer to use renamed schema exports" ``` --- ### Task 6: Update simulators and services **Files:** - `app/services/probability-updater.ts` - `app/services/standings-sync/index.ts` - `app/services/simulations/registry.ts` - `app/services/simulations/simulator-config.ts` - `app/services/simulations/types.ts` - `app/services/simulations/afl-simulator.ts` - `app/services/simulations/auto-racing-simulator.ts` - `app/services/simulations/bracket-simulator.ts` - `app/services/simulations/cs-major-simulator.ts` - `app/services/simulations/darts-simulator.ts` - `app/services/simulations/golf-simulator.ts` - `app/services/simulations/llws-simulator.ts` - `app/services/simulations/mlb-simulator.ts` - `app/services/simulations/nba-simulator.ts` - `app/services/simulations/ncaa-football-simulator.ts` - `app/services/simulations/ncaam-simulator.ts` - `app/services/simulations/ncaaw-simulator.ts` - `app/services/simulations/nfl-simulator.ts` - `app/services/simulations/nhl-simulator.ts` - `app/services/simulations/snooker-simulator.ts` - `app/services/simulations/tennis-simulator.ts` - `app/services/simulations/ucl-simulator.ts` - `app/services/simulations/wnba-simulator.ts` - `app/services/simulations/world-cup-simulator.ts` - `app/utils/sports-data-sync.server.ts` - `server/socket.ts` - `server/__tests__/timer-autodraft.test.ts` - [ ] **Step 1: Apply replacement rules** Same map as Task 4. Simulators are largely symmetric — one simulator's pattern will reveal all; batch 5 at a time and verify with typecheck. - [ ] **Step 2: Run simulator unit tests** Run: `npm run test:run -- app/services/simulations/__tests__` Expected: PASS. These tests consume Drizzle types extensively — a good regression canary. - [ ] **Step 3: Run full typecheck** Run: `npm run typecheck` Expected: PASS (0 errors). Any remaining errors indicate a file missed in earlier tasks. - [ ] **Step 4: Commit** ```bash git add app/services/ app/utils/ server/ git commit -m "refactor(services): update simulators and services for renamed schema" ``` --- ### Task 7: Full test suite + Cypress verification **Why:** Mechanical renames are easy to get 99% right and 1% broken in a way typecheck doesn't catch — especially relation-field renames (`.participant` → `.seasonParticipant`). - [ ] **Step 1: Run full unit test suite** Run: `npm run test:run` Expected: PASS. If failures, inspect each: a common miss is a relation accessor like `result.participant.name` that needs to become `result.seasonParticipant.name`. - [ ] **Step 2: Run a key E2E flow** Run: `npx cypress run --spec "cypress/e2e/draft-room.cy.ts"` (or equivalent flagship flow) Expected: PASS. E2E is the final guard — it exercises real relation loading. - [ ] **Step 3: Manual smoke in dev** Run: `npm run dev` Manually: 1. Load the admin dashboard for an in-flight golf sports season — verify participant list renders. 2. Load a league's draft room — verify participant cards render. 3. Open admin surface-elo page for the tennis sports season — verify Elo values load. Fix any broken UI before committing. (The rename may have missed a relation accessor somewhere.) - [ ] **Step 4: Final commit if any fixups were needed** ```bash git add -A git commit -m "refactor: fix straggler relation accessors after rename" ``` --- ### Task 8: Verify baselines still match **Why:** The entire point of Phase 1a is zero data change. Verify. - [ ] **Step 1: Re-capture and diff** Run: ```bash npx tsx scripts/capture-baseline.ts --out=/tmp/post-phase1a ``` (Update the script to accept `--out` or copy the baseline JSON files from `test-fixtures/baselines/` and re-run capture manually into `/tmp/post-phase1a/`.) Run: ```bash diff test-fixtures/baselines/qp-totals-pre-phase1.json /tmp/post-phase1a/qp-totals-pre-phase1.json diff test-fixtures/baselines/event-results-pre-phase1.json /tmp/post-phase1a/event-results-pre-phase1.json diff test-fixtures/baselines/surface-elos-pre-phase1.json /tmp/post-phase1a/surface-elos-pre-phase1.json ``` Expected: All three diffs empty. - [ ] **Step 2: Tag the branch** ```bash git tag phase-1a-complete ``` --- ### Task 9: Update documentation **Files:** - Modify: `docs/agents/database.md` — if it references the old table names - Modify: `docs/agents/domain-models.md` — if it describes these tables - [ ] **Step 1: Scan docs for old names** Run: `grep -l "participant_qualifying_totals\|participant_expected_values\|participant_results\|participant_surface_elos\|event_results.participant_id" docs/` Expected: list of docs to update. - [ ] **Step 2: Update each doc to use new names** For each file found, apply the rename map. Keep prose clear; where it helps readers, note "renamed from `X` in 2026-05". - [ ] **Step 3: Commit** ```bash git add docs/ git commit -m "docs: update table names in agent guides" ``` --- ## Self-Review Checklist Before marking this plan complete, verify: - [ ] `git log --oneline phase-1a-complete` shows ~8 commits corresponding to Tasks 0–9. - [ ] `grep -rn "from \"./participant\"" app/` returns zero results. - [ ] `grep -rn "participantExpectedValues\|participantQualifyingTotals\|participantSurfaceElos" app/ database/ server/ scripts/` returns zero results (excluding string literals in UI labels, which are fine). - [ ] `grep -rn "participantResults\b" app/ database/ server/` returns zero results. - [ ] `npm run test:all` is green. - [ ] `psql` confirms the renamed tables exist and the old names do not: ``` SELECT tablename FROM pg_tables WHERE tablename LIKE '%participant%' OR tablename LIKE '%season_participant%'; ``` Expected: `season_participants`, `season_participant_expected_values`, `season_participant_qualifying_totals`, `season_participant_results`, `season_participant_surface_elos`. None without the `season_` prefix. ## Rollback If Phase 1a must be undone: 1. `git revert` the commits from Tasks 1–9 (skip Task 0 baseline capture — keep fixtures). 2. Create a reverse migration that renames all five tables back and the column back, re-creates the old FK constraint. 3. Run `npm run db:migrate`. Phase 1b must not begin until Phase 1a is shipped, green in production for at least 24 hours, and the tag `phase-1a-complete` is on `main`.