Type tightened to enum
✕
Unsafe
The previous application version reproducibly fails against the proposed schema, and the failure is linked to a specific schema change.
orders.status becomes an enum without "shipped", a value v1 still writes.
Recorded example: genuine engine output generated Sat, 24 Oct 2026 18:30:06 GMT from the committed fixture. Run it again live below.
Baseline schema
Proposed schema
Previous versionshop-v1
2/2 test files passno database errors
1/2 test files passSQLSTATE 22P02, identical in all 2 runs
Current versionI-type-change
Not needed: ordinary CI covers this
Not configured
The outlined cell is the compatibility question. The verdict comes only from it, compared against the cell to its left.
Why
- test/orders.test.js: SQLSTATE 22P02 (invalid_text_representation) linked to column_type_changed:orders.status by R6 type error ↔ changed column type.
Next step
Make the migration backward compatible (expand/contract: keep the old shape until the old version is retired), then re-run the gate.
Starts a throwaway PostgreSQL and runs the full experiment, usually in under 15 seconds.
Evidence
Regression test/orders.test.js
- Observed
- test/orders.test.js passes on the baseline schema and fails in all 2 candidate runs.
- Database error
- SQLSTATE 22P02 invalid_text_representation — invalid input value for enum order_status: "shipped"
UPDATE orders SET status = 'shipped' WHERE id = $1
- Interpretation
- The failing statement uses orders.status (text → order_status).
- Rule
- R6 type error ↔ changed column type (strong correlation)
- Linked change
column_type_changed:orders.status
Schema changes
2 of 2 risk-bearing schema change(s) were exercised by the old application's tests in the control run.
| Change | Risk to old statements | Exercised by previous version |
|---|
| Column orders.status type text → order_status | breaking | Yes, 4 statements in test/orders.test.js |
Column orders.status default 'new'::text → 'new'::order_status Inserts that omit this column will store a different value. | behavioral | Yes, 2 statements in test/orders.test.js |
Runs
Proposed migration: 002_status_enum.sql · PostgreSQL 16.15 (Debian 16.15-1.pgdg12+2) · gate blocks the pipeline (exit 1)
| Run | Migrations | Test files | Database errors | Statements logged |
|---|
| control (old app × baseline schema) | 1/1 | 2/2 pass | 0 | 7 |
| candidate #1 (old app × proposed schema) | 2/2 | 1/2 pass | 1 (22P02) | 4 |
| candidate #2 (old app × proposed schema) | 2/2 | 1/2 pass | 1 (22P02) | 4 |
Download this report: JSONMarkdownJUnit XML
What this result does not cover
- Current application × proposed schema (no current app configured).
- Performance / lock-time analysis (out of scope).
- Skewcheck only observes code paths that the old application's tests execute; untested paths are reported as coverage gaps, not as safe.
- Coverage is identifier-level: a statement "touches" a changed object when it names the table (and column, or uses *). It is not a SQL parser.
- Correlation between an error and a schema change uses a fixed set of deterministic rules (R1–R6). Anything else is reported as ADVISORY, never UNSAFE.
- Performance, locking and migration runtime on production-sized data are not evaluated.
- Test files of one run share a database and run sequentially; ordering effects are identical across runs but are not isolated.
- Data-only migration effects are detected only when a test asserts on the affected data.