Adds database foreign keys for every reference between the QBit's tables (there were none). Changesets 12-26 in web_performance.changelog.sql, constraint names fk_<child table>_<column> (the same names kof22 uses, so this release is a no-op there).
history is kept (D8); SyncPagesSteps and WebPerfService never delete pages, they mark them not-in-sitemap; disable a page instead
regression.budget_id
budget
RESTRICT
a breach keeps its budget; disable a budget instead
Behaviour change: deleting a Tested Page or Budget that has history now fails with a foreign key error. The QBit itself never deletes from any table.
Orphan-safe upgrade
Each FK changeset first copies parent-less rows to qqq_fk_quarantine (created IF NOT EXISTS, shared DDL across qbits) and then nulls the column, or, for the two NOT NULL run_id columns, stores the full row (row_to_json) and deletes it (dbms:postgresql changesets 14 and 17). Each is guarded by a precondition on the constraint name, so a host that already added the constraint marks it ran. Changeset 12 indexes the FK columns that had no leading index.
Tests
New WebPerfForeignKeyTest (Testcontainers PostgreSQL 17 + Liquibase 4.33): applies changesets 1-11, inserts orphans, applies the rest, asserts the quarantine rows and repairs, asserts every table possible-value reference in the meta-data has an FK (exemption list with reasons, empty today), then re-runs the FK changesets against existing constraints (no-op). Red without the new changesets: 12 references reported missing.
The H2 harness skips dbms: changesets that are not for H2 (as Liquibase does); the FKs themselves apply on H2 too, and all existing tests pass.
## Summary
Adds database foreign keys for every reference between the QBit's tables (there were none). Changesets 12-26 in `web_performance.changelog.sql`, constraint names `fk_<child table>_<column>` (the same names kof22 uses, so this release is a no-op there).
| Child column | Parent | ON DELETE | Why |
|---|---|---|---|
| lab_finding.run_id (NOT NULL) | lab_run | CASCADE | owned detail of the run |
| lab_report.run_id (NOT NULL) | lab_run | CASCADE | owned detail of the run |
| comparison.change_event_id | change_event | CASCADE | computed for that change |
| regression.cause_change_event_id | change_event | SET NULL | optional pointer |
| lab_run.change_event_id | change_event | SET NULL | optional pointer |
| regression.first_run_id, checkpoint_run_id | lab_run | SET NULL | evidence pointers |
| lab_run / lab_finding / regression / comparison .target_id | target | RESTRICT | history is kept (D8); `SyncPagesSteps` and `WebPerfService` never delete pages, they mark them not-in-sitemap; disable a page instead |
| regression.budget_id | budget | RESTRICT | a breach keeps its budget; disable a budget instead |
Behaviour change: deleting a Tested Page or Budget that has history now fails with a foreign key error. The QBit itself never deletes from any table.
## Orphan-safe upgrade
Each FK changeset first copies parent-less rows to `qqq_fk_quarantine` (created IF NOT EXISTS, shared DDL across qbits) and then nulls the column, or, for the two NOT NULL `run_id` columns, stores the full row (`row_to_json`) and deletes it (`dbms:postgresql` changesets 14 and 17). Each is guarded by a precondition on the constraint name, so a host that already added the constraint marks it ran. Changeset 12 indexes the FK columns that had no leading index.
## Tests
- New `WebPerfForeignKeyTest` (Testcontainers PostgreSQL 17 + Liquibase 4.33): applies changesets 1-11, inserts orphans, applies the rest, asserts the quarantine rows and repairs, asserts every table possible-value reference in the meta-data has an FK (exemption list with reasons, empty today), then re-runs the FK changesets against existing constraints (no-op). Red without the new changesets: 12 references reported missing.
- The H2 harness skips `dbms:` changesets that are not for H2 (as Liquibase does); the FKs themselves apply on H2 too, and all existing tests pass.
- `./mvnw verify` green locally.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Adds database foreign keys for every reference between the QBit's tables (there were none). Changesets 12-26 in
web_performance.changelog.sql, constraint namesfk_<child table>_<column>(the same names kof22 uses, so this release is a no-op there).SyncPagesStepsandWebPerfServicenever delete pages, they mark them not-in-sitemap; disable a page insteadBehaviour change: deleting a Tested Page or Budget that has history now fails with a foreign key error. The QBit itself never deletes from any table.
Orphan-safe upgrade
Each FK changeset first copies parent-less rows to
qqq_fk_quarantine(created IF NOT EXISTS, shared DDL across qbits) and then nulls the column, or, for the two NOT NULLrun_idcolumns, stores the full row (row_to_json) and deletes it (dbms:postgresqlchangesets 14 and 17). Each is guarded by a precondition on the constraint name, so a host that already added the constraint marks it ran. Changeset 12 indexes the FK columns that had no leading index.Tests
WebPerfForeignKeyTest(Testcontainers PostgreSQL 17 + Liquibase 4.33): applies changesets 1-11, inserts orphans, applies the rest, asserts the quarantine rows and repairs, asserts every table possible-value reference in the meta-data has an FK (exemption list with reasons, empty today), then re-runs the FK changesets against existing constraints (no-op). Red without the new changesets: 12 references reported missing.dbms:changesets that are not for H2 (as Liquibase does); the FKs themselves apply on H2 too, and all existing tests pass../mvnw verifygreen locally.🤖 Generated with Claude Code