A database plan produced by NTNT 0.6.0 contains three unusually stubborn facts:
authorized: false
executable: false
automatic_safe: false
That is not the tool being coy. The plan can describe the intended SQL, the target database, the starting schema and the expected result in considerable detail. It still refuses to treat its own homework as permission to change anything.
NTNT 0.6.0 was released today with a new migration workflow for PostgreSQL and SQLite. Running numbered SQL files is familiar territory. The consequential part is the line it draws between knowing what would happen and being allowed to make it happen.
The plan is evidence, not a key
The workflow has four visible steps:
ntnt db status --database app --target staging --json
ntnt db plan --database app --target staging --out plan.json --json
ntnt db migrate --database app --target staging --plan plan.json --json
ntnt db verify --database app --target staging --json
Planning inspects the live catalog, checks the recorded migration history, replays the candidate in separate scratch storage and binds the result to a named target. The saved plan gets a digest covering the SQL, policy, target identity and observed starting state.
But handing that digest back on the command line would let the planner approve itself. Instead, execution requires the exact reviewed digest to arrive through a separately protected secrets provider. Before touching the schema, the migrator checks the target and starting state again. A stale plan, a different database or edited migration history fails closed.
This is a useful distinction wherever software agents prepare deployment work. An agent may be perfectly qualified to inspect a database and produce a review artifact without also having standing authority to alter production. Capability can be narrow without making the tool useless.
Failure gets a more honest vocabulary
Database migrations also have an awkward habit of failing after something has already committed. NTNT runs each supported migration with its checks, catalog verification and ledger entry in one transaction, then commits before starting the next file. If migration three fails, migrations one and two do not vanish for narrative convenience. The report preserves that committed prefix.
The nastier case is a lost acknowledgement after COMMIT. The database may have accepted the change even though the client never heard the answer. NTNT reconnects and inspects the ledger and catalog rather than blindly running the SQL again. If it still cannot establish the outcome, it reports unknown. That word is less comforting than “retrying,” but much safer.
The release also has an explicit path for adopting an existing database. It compares the current schema with a replayed, reviewed history and records an observed checkpoint. It does not pretend NTNT executed old migration files that predate the tool. The published customer-and-order rehearsal tests that distinction with populated SQLite data, then applies a later upgrade and checks both old and new application readers.
The boundary matters as much as the feature
This is not a universal database robot. The initial PostgreSQL adapter covers versions 16 through 18 and requires trusted local or separately secured transport because its migration connection does not yet support TLS. The supported SQL is deliberately narrower than PostgreSQL's full grammar. Concurrent indexes, general backfills and automatic schema repair remain outside the release. Its strong table locks also make this reviewed transactional tooling, not a claim of online migration magic.
Those limits make the central idea clearer. A migration system should know the difference between a plan, an approval, a committed change and an outcome it cannot prove. NTNT 0.6.0 gives each of those states a separate name. Databases are difficult enough without allowing optimism to become a transaction mode.