Skip to content

Commit 6021f21

Browse files
committed
Harden database migration and promotion lanes before expansion - PR_26167_207-database-lane-hardening
1 parent 9e8cd9e commit 6021f21

11 files changed

Lines changed: 1073 additions & 251 deletions

docs_build/database/backup-restore-lane.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,25 @@ Validation expectation:
3333
- the backup file is written outside tracked source paths
3434
- reports state only the backup path and success/failure, not the connection string
3535

36+
Backup metadata must be recorded without secrets:
37+
38+
- `backupName`
39+
- `databaseName`
40+
- `createdAt`
41+
- `migrationVersion`
42+
43+
Use `schema_migrations` to derive `migrationVersion`; do not use
44+
`platform_settings` for backup migration state.
45+
3646
## Restore Checklist
3747

3848
Restore is destructive. Do not run restore unless every checklist item is complete:
3949

4050
- Confirm the target `.env` is the intended database.
4151
- Run `node .\scripts\validate-runtime-connections.mjs` and record PASS/FAIL.
52+
- Validate the current `schema_migrations` state before applying restore.
4253
- Confirm the backup file path and checksum.
54+
- Confirm the backup metadata `migrationVersion`.
4355
- Confirm the application is stopped or in a maintenance window.
4456
- Confirm owner approval.
4557
- Type or record the exact operator confirmation phrase: `RESTORE CONFIRMED`.
@@ -68,8 +80,10 @@ pg_restore --dbname $databaseUrl --clean --if-exists --single-transaction $backu
6880
Restore reports must include:
6981

7082
- target host, port, and database name only
83+
- backup metadata: `backupName`, `databaseName`, `createdAt`, and `migrationVersion`
7184
- backup file path
7285
- backup checksum
86+
- migration state validation before restore
7387
- confirmation checklist status
7488
- validation result after restore
7589

docs_build/database/promotion-lane.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ DEV -> IST -> UAT -> PRD
88

99
The target name describes where the operator points the configured connection. It must not change application behavior.
1010

11+
This order is mandatory. Do not skip a target, promote backward, or promote a
12+
later target until the current target's `schema_migrations` state has been
13+
validated.
14+
1115
## Copy-Source Flow
1216

1317
Runtime, validation, and migration apply scripts load `.env` only.
@@ -26,9 +30,10 @@ Promotion is manual:
2630
3. Run `node .\scripts\validate-runtime-connections.mjs`.
2731
4. Run `node .\scripts\apply-database-ddl.mjs`.
2832
5. Run `node .\scripts\apply-database-dml.mjs`.
29-
6. Run `node .\scripts\validate-runtime-connections.mjs` again.
30-
7. Review `schema_migrations` before promoting the next target.
31-
8. Start or restart the runtime after validation passes.
33+
6. Run `node .\scripts\validate-database-drift.mjs`.
34+
7. Run `node .\scripts\validate-runtime-connections.mjs` again.
35+
8. Review `schema_migrations` before promoting the next target.
36+
9. Start or restart the runtime after validation passes.
3237

3338
Do not pass runtime environment parameters such as `--env`, `--environment`, or `ENVIRONMENT=<target>`.
3439

@@ -38,7 +43,8 @@ Do not pass runtime environment parameters such as `--env`, `--environment`, or
3843

3944
The migration apply lane records:
4045

41-
- `fileName`
46+
- `key`
47+
- `migrationName`
4248
- `migrationType`
4349
- `checksum`
4450
- `appliedAt`
@@ -54,6 +60,8 @@ Each target is eligible for the next promotion step only after:
5460

5561
- validation passes against the current `.env`
5662
- DDL/DML apply completes without checksum drift
63+
- drift validation confirms required tables, columns, indexes, constraints, and platform setting keys
64+
- `schema_migrations` matches the expected applied DDL/DML state for the target
5765
- repeat validation passes
5866
- the operator confirms the intended target connection without exposing secrets
5967

docs_build/database/runbook.md

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,22 @@ application behavior.
1515
`schema_migrations` is the authoritative migration state for DDL and DML apply
1616
history.
1717

18+
Required `schema_migrations` fields are:
19+
20+
- `key`
21+
- `migrationName`
22+
- `migrationType`
23+
- `checksum`
24+
- `appliedAt`
25+
- `appliedBy`
26+
1827
`platform_settings` is runtime settings data only. It may hold settings such as
1928
platform banners and future maintenance toggles, but it must not track, gate, or
2029
control migration apply state.
2130

31+
The optional current database version report is derived from
32+
`schema_migrations`; it is not stored in `platform_settings`.
33+
2234
## Validate
2335

2436
Run connection validation before and after apply work:
@@ -45,10 +57,10 @@ Apply DDL from `docs_build/database/ddl/`:
4557
node .\scripts\apply-database-ddl.mjs
4658
```
4759

48-
The apply lane records each file in `schema_migrations` with file name, type,
49-
checksum, applied time, and applied-by metadata. If an applied file checksum
50-
changes, the apply lane must fail visibly. Create a new migration file instead
51-
of editing an already-applied file.
60+
The apply lane records each file in `schema_migrations` with `key`,
61+
`migrationName`, `migrationType`, `checksum`, `appliedAt`, and `appliedBy`
62+
metadata. If an applied file checksum changes, the apply lane must fail visibly.
63+
Create a new migration file instead of editing an already-applied file.
5264

5365
## Apply DML
5466

@@ -76,9 +88,10 @@ node --use-system-ca .\scripts\apply-database-seed.mjs
7688
```
7789

7890
The DEV seed target keeps User 1, User 2, and User 3 as creator-only identities.
79-
DavidQ remains owner, admin, and creator. The seed script must refuse unsafe
80-
non-DEV targets unless a later approved operator lane explicitly changes that
81-
rule.
91+
DavidQ remains owner, admin, and creator. The seed script must refuse IST, UAT,
92+
and PRD targets and must confirm the owner role assignment before seed
93+
execution. A later approved operator lane is required before any non-DEV seed
94+
execution is allowed.
8295

8396
## Backup
8497

@@ -90,6 +103,8 @@ Minimum backup checklist:
90103

91104
- Confirm `.env` points at the intended database.
92105
- Confirm `pg_dump` is installed.
106+
- Record backup metadata: `backupName`, `databaseName`, `createdAt`, and
107+
`migrationVersion`.
93108
- Write backup files outside tracked source paths.
94109
- Record backup path and checksum only.
95110
- Do not write secrets, full URLs, service role keys, or dump contents to
@@ -104,7 +119,9 @@ must require explicit operator confirmation.
104119
Minimum restore checklist:
105120

106121
- Confirm `.env` points at the intended target database.
122+
- Validate the current `schema_migrations` state before applying restore.
107123
- Confirm the backup file path and checksum.
124+
- Confirm backup metadata, including `migrationVersion`.
108125
- Stop the application or enter a maintenance window.
109126
- Confirm owner approval.
110127
- Record the confirmation phrase `RESTORE CONFIRMED`.
@@ -120,22 +137,33 @@ Promotion is manual and must move in this order:
120137
DEV -> IST -> UAT -> PRD
121138
```
122139

123-
For each target:
140+
Exact operator workflow for each target:
124141

125142
1. Copy the selected `.env.<target>` file to `.env`.
126143
2. Confirm `.env` contains the intended database host, port, database name, and
127144
SSL mode without exposing secrets.
128145
3. Run `node .\scripts\validate-runtime-connections.mjs`.
129-
4. Run `node .\scripts\apply-database-ddl.mjs`.
130-
5. Run `node .\scripts\apply-database-dml.mjs`.
131-
6. Run `node .\scripts\validate-database-drift.mjs`.
132-
7. Run `node .\scripts\validate-runtime-connections.mjs` again.
133-
8. Review `schema_migrations` for the expected DDL/DML files.
134-
9. Start or restart the runtime.
135-
10. Promote the next target only after the current target passes.
146+
4. Create a backup and record backup metadata.
147+
5. Run `node .\scripts\apply-database-ddl.mjs`.
148+
6. Run `node .\scripts\apply-database-dml.mjs`.
149+
7. Run `node .\scripts\validate-database-drift.mjs`.
150+
8. Run `node .\scripts\validate-runtime-connections.mjs` again.
151+
9. Review `schema_migrations` for the expected DDL/DML files.
152+
10. Start or restart the runtime.
153+
11. Promote the next target only after the current target passes.
136154

137155
Do not use `platform_settings` as a migration gate or promotion gate.
138156

157+
## Owner Operations Boundary
158+
159+
Owner Operations remains status-first and execute-later until the database lanes
160+
are stable. It may show validation, apply, backup, restore, and migration
161+
history status, but it must not execute database operations from the browser.
162+
163+
The status surface currently routes through
164+
`src/dev-runtime/server/local-api-router.mjs`. That filename is retained as a
165+
legacy runtime filename for now; do not infer local database behavior from it.
166+
139167
## Rollback Guidance
140168

141169
Prefer forward-fix migrations for application schema corrections. Do not edit an

docs_build/database/seed/account/supabase-dev-identity-bootstrap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Use this only for the DEV Supabase project. Identity seeding is server-side only
55
## Bootstrap Order
66

77
1. Run `docs_build/database/ddl/account/supabase-identity-tables.sql` in the Supabase SQL editor or through an approved operator SQL path.
8-
2. Run `npm run validate:supabase-dev`.
8+
2. Run `npm run validate:runtime-connections`.
99
3. Confirm these checks pass through REST/API:
1010
- `Service role authentication`
1111
- `users table`
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# PR_26167_207-database-lane-hardening
2+
3+
Status: PASS
4+
5+
## Branch Validation
6+
7+
PASS - current branch is `main`.
8+
9+
## Review Resolution Checklist
10+
11+
| Finding | Status | Resolution |
12+
| --- | --- | --- |
13+
| Keep `schema_migrations` as the only DDL/DML apply authority | PASS | Migration apply and docs keep `schema_migrations` authoritative. `platform_settings` is documented as runtime settings only. |
14+
| PR201 promotion lane order and migration validation | PASS | `promotion-lane.md` now states mandatory `DEV -> IST -> UAT -> PRD` order and requires `schema_migrations` plus drift validation before promotion. |
15+
| PR202 backup metadata and restore migration validation | PASS | `backup-restore-lane.md` now requires `backupName`, `databaseName`, `createdAt`, and `migrationVersion`, and restore validates migration state before applying. |
16+
| PR203 drift validation expansion | PASS | `validate-database-drift.mjs` now validates tables, columns, indexes, constraints, and required `platform_settings` keys with exact missing-object diagnostics. |
17+
| PR204 DEV-only seed and owner role requirement | PASS | `apply-database-seed.mjs` refuses non-DEV database names and confirms DavidQ owner assignment before seed execution. |
18+
| PR205 Owner Operations warning | PASS | `runbook.md` documents `src/dev-runtime/server/local-api-router.mjs` as a legacy filename and keeps Owner Operations status-first, execute-later. |
19+
| PR206 exact operator workflow | PASS | `runbook.md` now documents validate, backup, apply DDL, apply DML, validate, and promote. |
20+
| Standard DB script names | PASS | Active package/database lane uses `validate-runtime-connections.mjs`, `apply-database-ddl.mjs`, `apply-database-dml.mjs`, and `apply-database-seed.mjs`; stale database doc command was updated. |
21+
| Remove active Supabase-specific DB tooling naming | PASS | Removed `validate:supabase-dev` from active `package.json` scripts. Compatibility wrapper files remain only as deprecated entrypoints. |
22+
| Add `schema_migrations` fields | PASS | Runner now creates/ensures `key`, `migrationName`, `migrationType`, `checksum`, `appliedAt`, and `appliedBy`; existing DB evidence includes those fields. Legacy `fileName` remains only for existing-table compatibility. |
23+
24+
## Migration State Evidence
25+
26+
- DDL/DML apply output reports: `Migration tracking fields: key, migrationName, migrationType, checksum, appliedAt, appliedBy`.
27+
- Current database columns observed: `key,fileName,migrationType,checksum,appliedAt,appliedBy,migrationName`.
28+
- `fileName` is retained in the current database for compatibility with already-applied records; active tracking uses `migrationName`.
29+
30+
## Validation Lane Report
31+
32+
Targeted DB validation only:
33+
34+
- `node --check scripts\database-migration-runner.mjs` - PASS
35+
- `node --check scripts\validate-database-drift.mjs` - PASS
36+
- `node --check scripts\apply-database-seed.mjs` - PASS
37+
- `node --check scripts\apply-database-ddl.mjs` - PASS
38+
- `node --check scripts\apply-database-dml.mjs` - PASS
39+
- `node --use-system-ca .\scripts\validate-runtime-connections.mjs` - PASS before apply and PASS after apply
40+
- `node .\scripts\apply-database-ddl.mjs` - PASS, processed=15, applied=0, skipped=15
41+
- `node .\scripts\apply-database-dml.mjs` - PASS, processed=15, applied=0, skipped=15
42+
- `node .\scripts\validate-database-drift.mjs --diagnostic-self-test` - PASS
43+
- `node .\scripts\validate-database-drift.mjs` - PASS, tables=34, constraints=146, indexes=115, platform setting keys=3
44+
- `node --use-system-ca .\scripts\apply-database-seed.mjs --unsafe-target-self-test` - PASS
45+
- `node --use-system-ca .\scripts\apply-database-seed.mjs --dry-run` - PASS; User 1-3 creator PASS, DavidQ owner/admin/creator PASS
46+
- `node -e "JSON.parse(require('fs').readFileSync('package.json','utf8')); console.log('PASS - package.json parsed')"` - PASS
47+
- `rg -n "validate:supabase-dev|apply:supabase|validate-supabase-dev|apply-supabase-dev-ddl" package.json docs_build\database scripts\database-migration-runner.mjs scripts\apply-database-ddl.mjs scripts\apply-database-dml.mjs scripts\apply-database-seed.mjs scripts\validate-database-drift.mjs` - PASS, no active database lane matches
48+
- `git diff --check -- scripts\database-migration-runner.mjs scripts\validate-database-drift.mjs scripts\apply-database-seed.mjs package.json docs_build\database\promotion-lane.md docs_build\database\backup-restore-lane.md docs_build\database\runbook.md docs_build\database\seed\account\supabase-dev-identity-bootstrap.md` - PASS
49+
- `git diff --check` - PASS
50+
51+
Skipped lanes:
52+
53+
- Playwright - SKIP, no Owner Operations UI/browser behavior changed; existing Owner Operations coverage remains from PR205.
54+
- Full samples smoke - SKIP, samples are not in scope.
55+
56+
## Manual Validation Notes
57+
58+
The hardened DB lane was manually reviewed against the review findings. `schema_migrations` remains the DDL/DML apply state, `platform_settings` remains runtime configuration only, seed is DEV-only and owner-gated, and Owner Operations remains a read/status surface until database lanes are stable.
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
docs_build/database/README.md
1+
docs_build/database/backup-restore-lane.md
2+
docs_build/database/promotion-lane.md
23
docs_build/database/runbook.md
3-
docs_build/dev/reports/PR_26167_206-database-runbook.md
4+
docs_build/database/seed/account/supabase-dev-identity-bootstrap.md
5+
package.json
6+
scripts/apply-database-seed.mjs
7+
scripts/database-migration-runner.mjs
8+
scripts/validate-database-drift.mjs
9+
docs_build/dev/reports/PR_26167_207-database-lane-hardening.md
410
docs_build/dev/reports/codex_review.diff
511
docs_build/dev/reports/codex_changed_files.txt

0 commit comments

Comments
 (0)