pgfence 0.6.1: Trust Contract fixes after the audit pass
pgfence 0.6.1 closes several false-negative paths in ORM extraction, inline foreign key scoring, constrained-domain analysis, stats-source precedence, and public boundary linting.
pgfence 0.6.1 is out today.
This is not a big-feature release. It is a Trust Contract release: the kind that makes the analyzer harder to fool, more explicit about what it cannot know, and less likely to imply safety when a migration was not fully understood.
Upgrade:
npm install -D @flvmnt/pgfence@0.6.1
What changed
TypeORM and Knex aliases no longer disappear
The extractor now tracks common alias shapes that previously created false-negative risk.
TypeORM:
const { manager } = queryRunner;
await manager.query('DROP TABLE users');
Knex:
const { schema } = knex;
await schema.dropTable('users');
Both examples now reach the analyzer and produce the expected destructive statement finding. The important point is not that these exact snippets are clever. It is that teams write migrations through framework objects and aliases all the time. If pgfence recognizes the base object, it has to keep following the common alias shapes.
Inline foreign keys include referenced-table stats
This statement is a column addition and a relationship change:
ALTER TABLE appointments
ADD COLUMN worker_id int REFERENCES workers(id);
The size-aware scorer now considers both the child table and the referenced table. If the referenced table has tens of millions of rows, that fact participates in risk escalation.
That keeps inline syntax from hiding the operational scope of the migration.
Constrained domains are visible through schema snapshots
Domains with constraints can make an otherwise simple ADD COLUMN much riskier:
ALTER TABLE users ADD COLUMN score positive_int;
If positive_int is a constrained domain and that fact is present in the schema snapshot, pgfence now reports the constrained-domain path. If the type looks custom but cannot be resolved from the snapshot, pgfence emits a visible caveat instead of silently treating it as a plain built-in type.
That is the right bias. A custom type is not automatically dangerous, but an unresolved custom type is not proven safe either.
Late lock_timeout ordering now follows emitted checks
The policy checker now derives late lock_timeout ordering from emitted ACCESS EXCLUSIVE checks.
That closes gaps for statements such as:
CLUSTERVACUUM FULL- inline foreign key column additions
- constrained-domain column additions
- row security changes
If the analyzer reports an ACCESS EXCLUSIVE operation, the policy layer can now use that same fact to decide whether SET lock_timeout came too late.
CLI stats-source precedence matches user intent
Command-line flags now win in the order users expect:
- explicit
--db-url - explicit
--stats-file - configured DB URL
- configured stats file
That means a user can temporarily switch a configured DB-backed project to a local stats snapshot without editing config.
Boundary checks got stricter
The public lint guard now catches bare dynamic imports into excluded implementation areas. The shell boundary guard already caught these. The lint layer now catches them too, including nested variants.
Stable defaults clarified
The docs now match the analyzer’s ADD COLUMN default behavior:
- constant defaults are metadata-only on PostgreSQL 11 and newer
- stable defaults such as
now()andCURRENT_TIMESTAMPare also treated as fast defaults - volatile defaults such as
clock_timestamp(),gen_random_uuid(), andrandom()remain high risk
That distinction matters because old migration advice often says “any default rewrites the table.” That was a good pre-PG11 rule. It is no longer precise enough.
Why this release matters
The highest-value release work is often unglamorous. It is not a new button. It is a missing alias, an unresolved custom type, a policy ordering edge case, a stats-source precedence bug, a lint rule that should have failed one more import form.
Those are the places where trust leaks.
pgfence’s job is to make the migration review conversation concrete: what locks, what blocks, what is safe, and what could not be proven. 0.6.1 tightens that loop after the 0.6 release and keeps the analyzer honest.
Full notes are on the releases page.