- Transpiler silent failures eliminated: 18 early-return paths in Knex (6) and Sequelize (12) transpilers that silently dropped migration statements now emit
ExtractionWarningwith descriptive messages. Previously, aknex.schema.dropTable()with a dynamic argument would vanish from analysis entirely. - Plugin crashes surfaced in structured output: plugin rule/policy errors now appear as
ExtractionWarningentries in JSON, GitHub, and SARIF output (not just stderr). A crashing custom plugin no longer silently passes CI. - Coverage calculation fixed: added
unanalyzableflag toExtractionWarningto distinguish truly unanalyzable statements (dynamic SQL, transpile failures) from informational warnings (builder API detection, conditional SQL advisories). Coverage percentage now accurately reflects what was and was not analyzed.
- Trace mode DB connection leak:
traceClientandobserverClientare now closed in thefinallyblock. Previously, if any error was thrown between connect and end, both connections leaked and the container could hang. - Policy ignore bleed:
fileIgnoredRulesnow only reads from the first statement'signoredRules(file-level comments), not from every statement. Apgfence-ignorecomment on a DDL statement in the middle of the file no longer suppresses file-level policy checks. lock-timeout-after-dangerous-statementnow suppressible: consistent with all other policy violations, this rule can now be suppressed via-- pgfence-ignore.- Stale
adjustedRiskin trace-merge: mismatch results now explicitly clearadjustedRiskso downstream code uses the recalculated risk, not the stale DB-size-adjusted value from static analysis. - NaN guard on timeout CLI options:
--max-lock-timeoutand--max-statement-timeoutnow throw a descriptive error on non-numeric values instead of silently disabling threshold checks. - Stats file error context: malformed
--stats-filenow shows the file path and specific parse error. - package.json error context: includes the actual error message and file path.
- Format auto-detection failure now emits an
ExtractionWarninginstead of silently falling back to raw SQL - Analysis crash now clears stale diagnostics (previously, old "safe" diagnostics would persist after a crash)
- Configuration fetch errors are now logged for non-capability errors (was a bare catch swallowing everything)
- Fixed 11 stale/inaccurate comments across rule files: REINDEX TABLE lock mode (SHARE, not ACCESS EXCLUSIVE), ATTACH PARTITION PG12+ behavior, ALTER TYPE ADD VALUE type-object lock clarification, DROP CONSTRAINT added to destructive header, policy.ts state machine description, analyze-text.ts temp-file comment, alter-column.ts text conversion accuracy, best-practices.ts varchar widening note, cloud-hooks.ts "open-source mode" clarification, add-column.ts non-constant default description, add-constraint.ts VALIDATE lock description
db-stats.ts: descriptive connection error messagestransaction-state.ts: accurate depth tracking JSDocdiagnostics.ts: UTF-8 multi-byte character notealter-column.ts: accurateclassifyTypeChangeJSDoc
- 393 tests (was 371 in 0.4.0)
- 10 new SARIF reporter tests (was zero coverage)
- DROP SCHEMA, DROP SCHEMA CASCADE, DROP CONSTRAINT tests with fixtures
- 6
adjustRiskboundary condition tests (exact thresholds: 9,999 vs 10,000, etc.) - REFRESH MATERIALIZED VIEW WITH NO DATA test
- 2 coverage calculation tests verifying informational warnings do not deflate coverage
Run your migrations against a real Postgres instance and verify every lock prediction:
pgfence trace migrations/*.sql- Spins up a disposable Docker container (
postgres:{version}-alpineor custom image) - Executes each statement and queries
pg_locks+ system catalog after every step - Diffs catalog snapshots to detect table rewrites (
relfilenodechanges), column modifications, constraint validation state, and index creation - Merges static analysis predictions with trace observations, producing a verification status for each check:
confirmed,mismatch,trace-only,static-only,error - Observer connection polls
pg_locksduringCONCURRENTLYstatements to capture transient locks that other tools skip entirely --pg-versiontargets specific Postgres versions (default: 17)--docker-imagesupports custom images (PostGIS, pgvector, TimescaleDB)--cifails on mismatches, execution errors, or risk threshold violations- Color-coded CLI reporter with verification status, duration, and trace-only findings section
sanitizeErrornow covers bothpostgres://andpostgresql://URL schemessnapshotcommand error handler callssanitizeError()(was missing, could leak credentials in CI logs)- Plugin loader rejects paths resolving outside the project directory (prevents code execution via malicious
.pgfence.json) stopContainerwrapped in try/catch so failures infinallyblocks no longer suppress the original error or leak container references- Container password passed to Docker is ephemeral (random 20-char base64url, never written to disk)
- Container binds to
127.0.0.1only
- trace-only findings: risk is now derived from the observed lock mode (was hard-coded to
LOWregardless of actual lock, allowing ACCESS EXCLUSIVE locks to pass--max-risk=medium) - mismatch findings: risk is upgraded to
max(static, trace-derived)when trace observes a stronger lock (was keeping the static risk, allowing dangerous migrations through CI) - trace-error entries: synthetic entries for failed statements now risk
MEDIUM(wasSAFE, displaying as green in the reporter) - CI error check: trace
--cinow fails onerrors > 0(was only checking mismatches)
- Error and cascade-error rows display
ERRORin the risk column instead of the placeholderSAFE/ACCESS SHARE - Dedicated "Execution Errors" section shows the actual error message for each failed statement
cascade-erroruses red coloring (was dim gray, indistinct fromstatic-only)tableRewrite: falsepreserved in output (was coerced toundefined, losing the "confirmed no rewrite" signal)
- Default minimum PostgreSQL version bumped from 11 to 14
rename-columnusesconfig.minPostgresVersion: no expand/contract recipe shown on PG14+ where RENAME is instant- Removed stale "PG10 and below" note from ADD COLUMN constant default safe rewrite
- Removed "PG12+" qualifiers from SET NOT NULL and REINDEX safe rewrites (unnecessary now that minimum is PG14)
- Removed phantom
TriggerChange/EnumChangeinterfaces that were declared but never populated
RESET ALLissued between file traces to prevent session state leakage (search_path, timeouts set by one migration file no longer affect subsequent files)
- Fixed FK message: "SHARE lock" corrected to "SHARE ROW EXCLUSIVE lock" on referenced table
- Fixed ATTACH PARTITION lock mode for PG12+ (SHARE_UPDATE_EXCLUSIVE, was ACCESS_EXCLUSIVE)
- Fixed REINDEX TABLE lock (SHARE, was ACCESS_EXCLUSIVE)
- Fixed
lock_timeout=0detection (protobufival.ival ?? 0fix) - Removed em dashes from all user-visible strings
- GitHub PR reporter URL corrected
- Extractor silent failures fixed in knex-transpiler and sequelize-transpiler
- 371 tests (was 176 in 0.3.2)
- New test suites:
tracer.test.ts,trace-merge.test.ts,trace-cli.test.ts - Risk derivation tests for trace-only, mismatch, no-downgrade, and trace-error scenarios
- Error and cascade-error reporter rendering tests
- Fixed ADD CONSTRAINT lock modes (SHARE ROW EXCLUSIVE, was ACCESS EXCLUSIVE)
- Fixed USING INDEX variants (SHARE UPDATE EXCLUSIVE)
- Added validate-constraint, add-pk-without-using-index, missing-idle-timeout tests
- Fixed CREATE TRIGGER lock mode (SHARE ROW EXCLUSIVE, was ACCESS EXCLUSIVE)
- Fixed REFRESH MATVIEW CONCURRENTLY lock mode (EXCLUSIVE, was SHARE UPDATE EXCLUSIVE)
- Fixed ALTER TYPE ADD VALUE lock mode on PG12+ (EXCLUSIVE, was SHARE UPDATE EXCLUSIVE)
- VS Code extension (LSP client)
- LSP server: diagnostics, code actions (safe rewrites), hover info
- 5 new rules: ban-char-field, prefer-identity, DROP DATABASE, ALTER DOMAIN, CREATE DOMAIN
- Inline
-- pgfence-ignorecomments - Schema snapshot support for definitive type analysis
- DB-size-aware risk scoring via
--db-urland--stats-file - GitHub PR comment reporter
- SARIF output format
- Plugin system for custom rules
- 6 ORM extractors: SQL, TypeORM, Prisma, Knex, Drizzle, Sequelize
- Initial release
- 15 DDL checks with lock mode analysis
- Safe rewrite recipes
- CLI and JSON output
- Policy checks (lock_timeout, statement_timeout, CONCURRENTLY in tx)