Skip to content

An AI Agent That Waits for Your Approval

A screencast of Ptah Assist: a model using Ptah's own tools to propose a schema change, with every write held until it is approved.

There are three ways to let a model change a database schema, and the choice decides what you have to trust.

The first is to let it write SQL and run it. This is the fastest to build and the hardest to review: what arrives is a statement, and the only way to know what it does is to read it. The second is to give the model a shell and a repository. That scales to anything, which is the problem: the blast radius is the filesystem, and an approval approves a command rather than a change.

The third is the one we built. The model gets the operations Ptah already has, each behind a permission, and every write is a patch against a digest you approve. The constraint that makes it fit: a schema change is not free text. It has a known shape, a target dialect, and checks that can run before and after it lands. An operation that knows those things can describe what it is about to do. A shell command cannot.

We recorded the result. Ptah Assist: An AI Agent for Database Schemas and Migrations is on our YouTube channel. It runs under three minutes and covers one session end to end: the configuration it starts from, the tools it can call, a schema change, the migration for it, and the trace of everything that ran.

Ptah Assist. A laptop on a desk showing the session’s opening card: a model
with Ptah’s own tools, working on a real project. It reads the schema you wrote
and the database you actually have, proposes a change, and writes nothing until
you say yes.

The session opens on the provider profile, because that is where both questions are answered. A profile names an endpoint, a model, and a credential reference, either env:NAME or file:/path. Ptah refuses a key written into the profile, so there is never a copy of one in a file you might share.

The command that follows carries the rest of the boundary. It names the workspace, the target dialect, the directories the assistant may read, and the artifact classes it may write to. Nothing outside those is reachable.

/tools lists what the model can call. The model does not write SQL and hope. It asks Ptah to read the declared schema, read the live database catalog, compose a patch, render DDL for a dialect, and validate. What comes back is Ptah’s answer rather than the model’s recollection of one.

The distinction shows up in the footer we print under every answer. It reports how many tool calls ran, and how many bytes of project content reached the provider. An answer with no tool behind it looks exactly like a verified one, and that line is the difference.

Terminal window
Ptah is asking for artifact.write:schema.
apply a 1-file patch to the schema artifact
artifact: schema
directory: /home/developer/projects/ptah-assist-lab/workspace/schema
base digest: sha256:c2faa21a30324714af7e7de289200af7f5a84908223ba1cba094b4a9
result digest: sha256:b02e120da7ea70fbd48e4908f7aeaa45abc4f8d2f87be9da88c61e
update: schema.yaml
This approval covers exactly sha256:0dc192a72d9468887c3295c6e595fe8976a2186497
Allow once
Allow for this session
> No

The prompt names the artifact class, the directory, the file, and the digest the patch was composed against. Approving it approves that patch and no other. If the file moves underneath the token, the digest stops matching and the apply is refused rather than replayed against something else.

After the write lands, Ptah runs its own gates over the result and reports which passed. The migration goes through the same sequence.

Near the end, with the tool trace on, one call is refused:

Terminal window
refused render_schema
schema_source_unreadable: error parsing schema file: schema file does not exist...
ok render_schema
dialect="sqlite" statements=["CREATE TABLE \"users\" (\n \"id\"...

The model named a path the schema reader could not open. Ptah refused, said why, and the model called it again with a path that worked.

We left it in because it is the mechanism working. A recording in which nothing is ever refused demonstrates editing rather than a product. A refused call with its reason beside it explains the trace better than a paragraph about the trace would.

The model is one we host privately for research. Its name and endpoint in the recording are placeholders, consistently replaced, so the session cannot be reproduced against the same model. The shape of it reproduces against any OpenAI-compatible endpoint you can reach. The profile in the recording lists three, and one of them runs on the machine and carries no credential at all.

The workspace is SQLite and its schema is YAML. Ptah reads a desired schema from SQL, YAML, HCL, and DBML as well as from Go annotations, so nothing in the session depends on a Go project. We chose it deliberately. The recording is about the surface, not about one of its front ends.

The session does not apply a migration to a database. That capability was not granted, and the model reports it when asked what it can reach: “No row reads, no SQL execution, no migration applies (all denied).” The migration is written to a file and left there.

The interactive terminal in the recording is newer than the current release. The permission model it shows is not: scoped artifact classes, a preview, and an approval per patch are what Ptah 0.7.0 already does.

Ptah Assist: An AI Agent for Database Schemas and Migrations

To read the mechanism rather than watch it, ptah assist context prints what a question would send before anything is sent, and /trace inside a session prints what every tool call returned.