# Feature matrix

Ptah and Atlas capabilities side by side, with the evidence behind every row.

Source: https://docs.ptah.run/v0.8.0/atlas/feature-matrix/

For each capability, the matrix records what Ptah does, what the open Atlas
community binary does, and what Atlas keeps outside its community build. Every
row cites the evidence it rests on.

It is a status index, not an argument. The measured evidence is on
[Conformance](../conformance/), the differences Ptah keeps on purpose are on
[Compatibility differences](../retained-divergences/), and the Atlas-documentation
crosswalk is on [Atlas docs coverage](../docs-coverage/).

## How to read the tables

| Symbol | Meaning |
| --- | --- |
| ✅ | Supported today |
| 🟡 | Partial. The difference column states what is missing |
| ❌ | Not implemented |
| 🔷 | Ptah does this job in its own form. What is missing is wire compatibility with a hosted, account-bound service, which an independent implementation cannot obtain |
| ➖ | Does not apply to that product |
| ❔ | Not established by the evidence this page uses |

🔷 and 🟡 answer different questions, and the difference decides whether a
reader should wait for a release. 🟡 means the work is unfinished: the
difference column names the issue that owns it, and a later Ptah closes it. 🔷
means the capability is already here under a Ptah spelling, running against
infrastructure the team already operates, and the only thing absent is speaking
the wire protocol of a service somebody else hosts behind their own accounts.
No release closes that, so a 🔷 cell carries no tracking issue. A capability
that is merely incomplete is never 🔷, whatever it sits next to.

🔷 appears in the **Ptah** column only. The Atlas columns describe the hosted
product itself, where the distinction does not arise.

Each table has the same columns. **Ptah**, **CE**, and **Pro** carry one symbol
each:

- **Ptah** — the native `ptah` binary plus the separate `ptah-compat` drop-in.
- **CE** — the pinned Atlas community binary, version 1.3.0, which the
  conformance harness runs against. The pin itself lives in
  `scripts/atlas-ce-reference.lock`, which is what a bump changes.
- **Pro** — capabilities Atlas keeps outside its community build, per the
  [Atlas feature availability](https://atlasgo.io/features) page and
  [pricing page](https://atlasgo.io/pricing).

Every Atlas cell has to come from an Atlas-side source: the command, usage, and
flag inventory the conformance harness reads out of the pinned community
binary, measured behavior of an Atlas binary, or a classification Atlas
publishes. Measurement outranks published classification when they disagree.
Where nothing settles a question, the cell is ❔ rather than a guess.

## Atlas plans are not the CE column

Atlas's public plans are Starter (free), Pro, and Enterprise, and Atlas's own
[pricing page](https://atlasgo.io/pricing) classifies capabilities by plan.
That classification and the **CE** column answer different questions: the CE
column reports what the pinned community binary does, and
the two diverge in both directions. Both examples below were measured on
2026-08-01 against CE v1.2.0:

- The pricing page places migration linting outside the Starter plan, yet the
  CE binary runs `migrate lint` and reports destructive changes.
- The pricing page checks ERD visualization for Starter, yet the CE binary
  rejects `schema inspect --web` as an unknown flag; the ERD lives in the
  hosted service, not in the binary.

Where the pricing page settles a Pro-side question, the Pro column cites it.
Where plan marketing and measured binary behavior differ, the measured behavior
wins the CE cell and the difference column records the tension. The
schema-object rows are the clearest case: the HCL reference marks `partition` a
Pro feature and the pinned community binary plans `PARTITION BY RANGE` anyway,
while a live schema holding a domain, a composite type, a range type, a
sequence and an extension inspects on the same binary to nothing but its one
table. Both readings are in the row.

:::caution
A ✅ in the Ptah column means the capability works, not that it is
byte-identical to Atlas. Ptah is an independent pre-GA implementation, and the
conformance repository states plainly that no number it produces is a
full feature-set parity test. Read the difference column before relying on a
row for a migration decision.
:::

## At a glance

Across the 193 capabilities below:

| Reading | Count |
| --- | --- |
| Ptah supports it fully | 181 |
| Ptah supports it with a stated limitation | 1 |
| Ptah does not implement it | 0 |
| Ptah covers it in its own form, against a hosted service it cannot interoperate with | 11 |
| Ptah and Atlas CE both support it | 63 |
| Ptah implements it openly where Atlas gates it behind Pro or Cloud | 53 |
| Ptah has it and neither Atlas edition does | 27 |
| Atlas CE has it and Ptah does not, or only in part | 0 |
| An Atlas column is ❔ — not established by this page's evidence | 4 |

Every 🟡 and every ❌ in the Ptah column names its specific limitation and the
issue that owns it, reproduced against a binary built from this repository;
Atlas-column verdicts rest on the cited Atlas-side sources only. A 🔷 names no
issue on purpose, because nothing an independent implementation can build
closes it. Confirmed gaps are tracked in
[#926 to #942](https://github.com/stokaro/ptah/issues/926) and [#944](https://github.com/stokaro/ptah/issues/944).

The command surface is counted separately, because it is measured rather than
assessed. The conformance harness inventories every command in the pinned Atlas
CE binary and compares it with the `ptah-compat` surface: 19 of the 37
inventoried commands are open parity targets, and they match on help usage and
flags across 107 observations with one gap — `schema inspect --include`, a
Pro-surface flag the pinned CE binary does not register and Ptah implements
openly ([#951](https://github.com/stokaro/ptah/issues/951)). The remaining 18
are registry, Cloud, or Pro verbs that are not drop-in targets. Ptah implements
seven of them as open capabilities regardless.

## Schema sources

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| Annotated Go models from a live database | ✅ | ❌ | ❌ | Writes Ptah annotation source from introspection, with optional db/json tags. No Go-model generator in the CE inventory or Pro list. |
| Atlas CE inspect HCL parses back into Ptah | ✅ | ✅ | ✅ | 30 differential observations parse Atlas `schema inspect` HCL on PostgreSQL, MySQL and SQLite with zero schema-fact mismatches. |
| Atlas HCL data "external_schema" | ✅ | ❌ | ✅ | Ptah evaluates the data source and runs the program, gated behind `--allow-external-schema`/`PTAH_ALLOW_EXTERNAL_SCHEMA`. Community Atlas rejects `data.external_schema`. |
| Composite multi-source desired schema | ✅ | ❌ | ✅ | Repeatable `--root-dir`/`--schema-file` merge into one schema; conflicts error. Repo docs cite composite_schema as an Atlas Pro data source. |
| Desired-schema artifacts in an OCI registry | ✅ | ❌ | ✅ | `ptah schema push/pull` publish and fetch canonical HCL resolved from Go, YAML, HCL or SQL sources. Verified round trip against registry:2. |
| Directory of .hcl files as one schema source | ✅ | ✅ | ✅ | `--schema-file dir` and `--to file://dir` read a directory of .sql or .hcl files in filename order as an ordered script: mixed formats, an empty directory, a subdirectory and a redeclaration refuse. |
| External program / ORM loaders | ✅ | ✅ | ✅ | `--schema-cmd` or `ptah.yaml` external_schema (needs `--allow-external-schema`) runs a program without a shell emitting SQL, HCL, or YAML. |
| Go struct annotations | ✅ | ❌ | ❌ | Ptah parses //ptah:schema:* comments into the desired schema. Atlas's route to Go models is an external ORM provider program. |
| HCL foreign_key deferrable | ✅ | ❌ | ❌ | `deferrable` and `initially` are carried through HCL, the IR, the renderer and the PostgreSQL catalog read. The community binary plans no DEFERRABLE for the same file. |
| HCL function calls in schema files | ✅ | 🟡 | 🟡 | sql() reduces to its SQL everywhere, and all 67 names the schema evaluator registers evaluate. uuid is a type, not a function. sql() inside check.expr and index.where is a retained divergence. |
| HCL locals, lock, atlas, dynamic/for_each | ✅ | 🟡 | 🟡 | `locals` is evaluated and `local.x` resolves. `dynamic` blocks expand: for_each, labels, iterator and content. `lock` and `atlas` are accepted and reported as ignored. |
| HCL names outside the parsed subset | ✅ | ✅ | ❌ | `ptah-compat` accepts an unmodeled top-level name whose body names a declared schema and reports it as ignored; `schema.nope` exits 1 on both. Native `ptah` refuses by name. |
| HCL table and column child blocks | ✅ | 🟡 | ✅ | column, primary_key, index, unique, foreign_key, check, partition, row_security, constraint, platform; column nests as, identity, platform. The binary drops row_security; ptah-compat plans it. |
| HCL top-level blocks Ptah parses | ✅ | 🟡 | ✅ | schema, enum, table, extension, sequence, domain, composite, range, function, view, materialized, trigger, policy, role, permission, data. The community binary plans DDL for table and enum. |
| HCL variable blocks and var.* references | ✅ | ✅ | ✅ | `variable` blocks bind `var.x`, `--var name=value` overrides them, and a typed variable with no value exits 1 with `missing value for required variable "x"` — the community binary's own text. |
| Live database as desired state | ✅ | ✅ | ✅ | One connectable DB URL can be the desired side of compat schema apply/diff and migrate diff; `ptah db read` introspects natively. |
| Live database to Go annotation source | ✅ | ❌ | ❌ | `ptah introspect` writes annotated Go models from a live DB; repo docs record Go annotations as a first-party Ptah workflow. |
| Migration directory as a source | ✅ | ✅ | ✅ | Atlas-format directory with `atlas.sum`, replayed on a required `--dev-url`. Works on `ptah schema inspect` and compat apply/diff/migrate diff. |
| Ptah-only HCL schema extensions | ✅ | ➖ | ➖ | platform/override per dialect, EXCLUDE constraint block, column enum, table checks/custom, index ops, ClickHouse granularity, seed data block. |
| SQL DDL schema files | ✅ | ✅ | ✅ | Accepted by native `--schema-file` and compat `--to`/`--from`. Reads back every object kind Ptah renders; DO blocks, COMMENT ON and raw routine bodies still parse and are dropped. |
| YAML schema files | ✅ | ❌ | ❌ | Strict parser; unknown keys fail. Repo docs list Atlas OSS data sources as SQL, HCL, external schema, and remote/template dirs. |

</div>

## Direct schema changes

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| `--dry-run`, `--auto-approve`, and `--edit` | ✅ | ✅ | ✅ | All three registered and functional; `--edit` opens $VISUAL/$EDITOR and the edited SQL is applied. `--dry-run` and `--auto-approve` exclude each other on the command line, as on CE. |
| `--exclude` glob and type selectors | ✅ | ✅ | ✅ | Resource globs, one final [type=...], and leading [type=schema]. Qualified globs reach every kind. A column selector works in both scopes, and the depth rule matches the pinned binary exactly. |
| `--include` resource selectors | ✅ | ❌ | ✅ | CE registers `--include` on apply/diff but aborts it as non-community, and registers none on inspect. Ptah has all three, with union semantics and cross-scope dependency diagnostics. |
| `--schema` / -s scoping of both sides | ✅ | ✅ | ✅ | Schema names scope resources; non-extension matches carry extensions as support. Diff refuses exact PostgreSQL-family server namespaces before SQL because catalogs do not round-trip safely. |
| `schema diff --export` | ✅ | ❌ | ✅ | An `exporter` block declares a Go template and an env's `exporter` attribute selects it; `--export` renders through it, on both `schema diff` and `schema inspect`. |
| `schema inspect --include` filtering | ✅ | ❌ | ✅ | Compat and native inspect select top-level resources with the apply/diff selector engine. CE rejects the flag as unknown. A selection matching nothing renders nothing, exits 0, and says so on stderr. |
| `schema inspect --output` | ✅ | ❌ | ✅ | `-o/--output` writes the rendered schema to a file instead of stdout, published atomically so a reader never sees a partial document. |
| An include selector matching neither diff side fails closed | ✅ | ❌ | ❌ | Compat and native retain `--include`; no-match exits nonzero. Qualified PostgreSQL extension creates preserve installation schema; moves fail before SQL. |
| Apply advisory lock, `--lock-timeout`, `--lock-name`, `--skip-lock` | ✅ | 🟡 | ✅ | Real locks on PostgreSQL, YugabyteDB, MySQL, MariaDB, SQL Server. ptah-compat notes and runs others unlocked; native ptah refuses a typed `--lock-timeout`. `--lock-name`/`--skip-lock` are Pro surface. |
| Compat inspect block superset opt-in | ✅ | ❌ | ❌ | Compat inspect omits unreferenced extension, sequence and policy blocks; an env variable restores them. |
| Desired-state sources for `--to` and `--from` | ✅ | ✅ | ✅ | Files, a file:// directory of .sql or .hcl schema files, one DB URL, one atlas.sum dir, or env://; atlas:// fails early. |
| Dev-database rehearsal before apply | ✅ | ✅ | ✅ | Dev DB reset, target schema recreated, exact plan rehearsed, under `--dry-run` too; dev==target and failed rehearsal abort. A non-database `--to` requires `--dev-url`. |
| Drift detection against desired schema | ✅ | ❌ | ✅ | Native `ptah schema drift`: `--severity`, `--exit-code`, `--ignore`, text/json/github-actions. Atlas Cloud drift monitoring is out of scope. |
| Exclude selector that matches nothing is diagnosed | ✅ | ❌ | ❌ | A selector naming no object warns on inspect and diff and exits 1 on apply, and only a filter that asked it may call it empty. `PTAH_ATLAS_ALLOW_UNMATCHED_EXCLUDE` restores permissive behavior. |
| Exclude subtracts a named schema and its contents | ✅ | ✅ | ✅ | A one-part selector names a schema by catalog or quoted spelling, and it leaves with all contents. Case-preserving names remain addressable without collapsing case or quoted whitespace. |
| Exclude subtracts sequences, domains, composite types and range types | ✅ | ❔ | ✅ | `--exclude` reaches the same object kinds `--include` selects. Before, these four were read and cloned but never offered to a pattern, so excluding one was a silent no-op that still planned its DROP. |
| Go-template `--format` output | ✅ | ✅ | ✅ | schema apply registers the shared helper set; schema inspect registers its own (base64url, hcl, json, mermaid, sql, split, write). schema diff registers `sql` by default and the rest behind an opt-in. |
| Inspect `--exclude` field selectors | ✅ | ❌ | 🟡 | Six resource types subtract a field: `version` on an extension, `comment` on the rest. A field Ptah cannot subtract is refused, and the refusal names the ones that work. |
| Inspect non-database sources via `--dev-url` | ✅ | ✅ | ✅ | Schema file, `atlas.sum` migration dir, or env:// is materialized on a reset dev DB then introspected; without `--dev-url` it fails. |
| Inspect split/write file exports | ✅ | ❌ | ✅ | `{{ hcl . \| split \| write "dir" }}` writes object/schema/type trees; pinned Atlas CE rejects split, write, hcl as non-community. |
| Inspected document declares what it does not describe | ✅ | ❌ | ❌ | A compat inspect document records the block kinds it omitted, in its own header and in every split member, so a later apply reads the omission as unknown rather than as deletion intent. |
| JSON output for native schema diff | ✅ | ➖ | ➖ | Native ptah schema diff `--format` json emits a machine-readable statements document; the Go-template row only states that {{ json . }} fails on the compat diff, leaving native JSON unstated. |
| Local pre-approved plan files | ✅ | ❌ | ✅ | `schema plan` writes Atlas `.plan.hcl` by default (`.json` keeps the native plan); `apply --plan` reads both, Atlas-authored included, verified by replay against `--to` plus an end-state check. |
| schema apply against a live database | ✅ | ✅ | ✅ | Diffs `--url` against the `--to` desired state, prints the SQL plan, applies after confirmation. Verified end to end on SQLite. |
| schema clean | ✅ | ✅ | ✅ | `--include`/`--exclude` narrow cleanup. PostgreSQL-family scoped drops are dependency-safe and transactional, and a dependent left outside the selection is named before any DROP runs. |
| schema diff between two schema states | ✅ | ✅ | ✅ | SQLite rebuilds a table for changes ALTER TABLE cannot express, including one other tables refer to: the plan brackets itself in PRAGMA foreign_keys. |
| schema fmt (HCL canonical layout) | ✅ | ✅ | ✅ | Formats .hcl paths recursively and prints only changed files. Native `ptah schema fmt --check` adds a no-write CI gate. |
| schema inspect to HCL, SQL, or JSON | ✅ | ✅ | ✅ | Default HCL; rendered HCL/SQL/JSON use explicit helper templates. Bare and whitespace-wrapped hcl/sql/json are literal template text. Native shorthands still render and add file export. |
| Schema-qualified exclude globs for enums and functions | ✅ | 🟡 | ✅ | Enums and functions match schema-qualified globs on the rule tables and views use, and the match reaches the planned DROP. The community binary matches `app.mood`; it reports no functions. |
| Verb `schema stats` | ✅ | ❌ | ✅ | Ptah implements it as `schema stats inspect` on the compat surface and `schema stats` natively, emitting OpenMetrics counts for 18 object kinds. Unlike Atlas it accepts SQLite. |
| Verb `schema validate` | ✅ | ❌ | ✅ | `ptah schema validate` and the compat verb report every structural problem in a desired state without a database; exit 1 when any is found. `--no-skipped` also fails a dropped declaration. |

</div>

## Versioned migrations

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| `--dir` defaults to `file://migrations` | ✅ | ✅ | ✅ | Every migrate verb documenting a `--dir` default uses `file://migrations`. Never a fallback: the flag, `PTAH_DIR`, `PTAH_MIGRATIONS_DIR` and `atlas.hcl` outrank it, and `atlas.sum` is still gated. |
| An empty migration directory is not a checksum error | ✅ | ✅ | ✅ | `ptah-compat migrate validate` and `migrate lint --latest` exit 0 on a directory holding no migration files. Native `ptah migrations validate` and `ptah migrations lint` keep their refusals. |
| Apply pending migrations (apply/up) | ✅ | ✅ | ✅ | Dry runs read stored revisions and select only pending files. Per-migration timeouts reach every target whose server takes one; a dry run defers later checks. |
| Atlas R-suffixed (`1R_`, `R__`) migration execution | ✅ | ✅ | ✅ | Both execute a native Atlas `R` or `<number>R` file once and record its version token, and neither reapplies it when the body changes: reapply-on-checksum is a Flyway feature. |
| Atlas SQL template migrations (`--atlas-env`) | ✅ | ❌ | ❌ | Go-template actions in Atlas-format migration files render before execution with .Env set by `--atlas-env`; sibling *.sql files supply shared {{ template }} definitions. CE runs the braces as SQL. |
| Atlas txtar migration sections (-- atlas:txtar) | ✅ | ❌ | ✅ | `-- atlas:txtar` executes migration.sql/down.sql and enforces checks.sql plus ordered checks/*.sql, including atlas:assert oneof; unrelated files are ignored. CE runs every section as plain SQL. |
| Atlas-format checkpoint output | ✅ | ❌ | ✅ | `migrate checkpoint --dir-format atlas` writes the single `-- atlas:checkpoint` file plus atlas.sum, and is compat's default; `--dir-format ptah` writes the reversible pair. Up-only, as Atlas is. |
| Baseline an existing database | ✅ | ✅ | ✅ | Ptah adds a standalone baseline verb with `--shadow-db` verification and `--dry-run`; CE exposes baselining as migrate apply `--baseline`. |
| Create an empty migration file | ✅ | ✅ | ✅ | Native create writes an up/down pair; compat new writes the selected layout's skeleton for atlas and all five external formats, refreshes `atlas.sum`, `--edit` opens $VISUAL or $EDITOR on atlas. |
| Direct Flyway revision identity | ✅ | ✅ | ✅ | Flyway tokens persist; numeric keys order them. Empty identities render as "". Baselines use raw-token order. A repeatable target preserves history CE deletes; unknown roles and ties fail closed. |
| Directory integrity file and execution gate | ✅ | ✅ | ✅ | Both sums support hash/validate. Native SQL execution gates hashed dirs; compat apply/status/set/new/diff/down do too. Import verifies source sums. Replay uses one verified snapshot and refuses drift. |
| Directory maintenance: edit, rebase, rm | ✅ | ❌ | ✅ | Each rewrites `ptah.sum`/`atlas.sum` and refuses a migration applied in `--db-url` unless `--force`; CE aborts all three as non-community verbs. |
| Dynamic down planning (`migrate down --plan`) | ✅ | ❌ | ✅ | `--plan` builds the target schema on `--dev-url` and derives the rollback from the difference, so a migration with no down body is revertible. `--to-tag` and `--skip-checks` land too. |
| Execution order (`--exec-order`) | ✅ | ✅ | ✅ | linear fails on a pending migration below the current version, linear-skip leaves it pending, and non-linear applies it. Atlas chained revision hashes remain valid after an insertion. |
| External `--dir-format` outside `migrate import` | ✅ | ✅ | ✅ | Five external layouts read and write on `migrate new` and `migrate diff`, each with a no-transaction requirement in its own format. `edit`, `rebase`, `rm`, `test` and `checkpoint` refuse them by name. |
| Failed rollback state is recorded and recoverable | ✅ | ❌ | ❌ | Ptah records failed rollback direction, error, and completed-statement count in both revision-table formats; compat keeps the Atlas schema but does not copy Atlas's hidden failed-down state. |
| Flyway repeatable (`R__`) migration import | ✅ | ✅ | ✅ | Compat import converts `R__` to a one-time migration ordered last, as the format requires. Editing the body is then refused, naming the conversion and both remedies. |
| Generate migrations from a schema diff | ✅ | ✅ | ✅ | diff and new stamp the UTC second, stepping past a version already taken; checkpoint and rebase bump past the newest. A plan mixing transaction modes is split into ordered files. |
| migrate apply `--allow-dirty` semantics and the not-clean adoption gate | ✅ | ✅ | ✅ | Exact-identity retries require the current provider to own the dirty body; the flag also permits unmanaged-object adoption. Recovery preserves the committed prefix. |
| Migration checkpoints (squash history) | ✅ | ❌ | ✅ | Replays the directory on `--shadow-db` into a cumulative checkpoint: the ptah reversible pair, or Atlas's single `-- atlas:checkpoint` file under `--dir-format atlas`. CE gates the verb. |
| Migration import from other tools | ✅ | ✅ | ✅ | Native import writes Ptah format; compat import writes Atlas format and orders `R__` last. All four Liquibase serializations import; unconvertible changelog constructs are refused by name. |
| Migration linting | ✅ | 🟡 | ✅ | CE registers `migrate lint` with Open rules; its features page marks the CLI Pro. Compat requires `--dev-url`. `--latest 0` disables latest selection but preserves Git; opt-ins lift each precondition. |
| Migration lock, lock timeout, `--lock-name`, `--skip-lock` | ✅ | 🟡 | ✅ | Compat `--lock-timeout` bounds directory and dev-db locks; native `migrations up`/`down`/`baseline` refuse `--migration-lock-timeout` where the dialect cannot lock. `--lock-name` is Pro. |
| Migration status report | ✅ | ✅ | ✅ | Compat status mirrors the Atlas default report shape and renders Go templates over .Env, .Available, .Applied, .Pending, .Current, .Next. Native ptah keeps its own block. |
| Online DDL routing via gh-ost or pt-osc | ✅ | ❌ | ❌ | ptah.yaml online_ddl.tool (ghost\|pt-osc), threshold_rows, args, and fallback (error\|plain) route large-table ALTERs through an online-DDL tool during migrations up/down. CE has no such routing. |
| Pre-migration database backups (`--pg-dump-to`) | ✅ | ❌ | ❌ | `--pg-dump-to` writes a pg_dump custom-format backup and `--mysqldump-to` a SQL backup before applying or rolling back; ptah.yaml key migration.pg_dump_to. |
| Pre-migration webhook and shell hook gates | ✅ | ❌ | ❌ | `--webhook` POSTs migration metadata and requires HTTP 200; `--pre-up-hook`/`--pre-down-hook` run a shell command that must exit 0, else the run aborts. Also ptah.yaml migration.webhook/pre_up_hook. |
| Prometheus metrics endpoint (`--metrics-addr`) | ✅ | ❌ | ❌ | migrations up, down, and status serve a Prometheus /metrics endpoint at the given address for the run. |
| Repair dirty or partial revision state | ✅ | ❌ | ❌ | Under the migration advisory lock, `--resume-from` verifies the committed prefix, then finishes up before marking applied or down before removing the revision. Atlas CE has no repair verb. |
| Report of an ignored `--dir` URL query key | ✅ | ❌ | ❌ | Only `?format=` selects a layout. On the eight verbs accepting a `--dir` query, other keys are ignored, named on stderr, exit 0 unchanged; `PTAH_STRICT_DIR_QUERY=1` refuses. Six verbs take no query. |
| Revision table format and placement | ✅ | ✅ | ✅ | `--revision-format` ptah\|atlas plus `--migrations-table` and `--migrations-schema`; the compat path defaults to Atlas rows. |
| Roll back applied migrations (down) | ✅ | ❌ | ✅ | Ptah validates all down bodies before changing state, dry-run reports separate preflight rejection from attempted rollback, and `--plan`, `--to-tag` and `--skip-checks` are implemented. |
| Set revision state to a version | ✅ | ✅ | ✅ | Removes revision rows above the target, keeps rows at or below it, and inserts missing rows through it as manually set. |
| Structured JSON log output (`--log-format`) | ✅ | ❌ | ❌ | migrations up, down, and status take `--log-format` text\|json and `--log-level` debug\|info\|warn\|error for machine-readable run logs. |
| Transaction modes (`--tx-mode` file/all/none) | ✅ | ✅ | ✅ | File/all/none behavior is tested and none-mode partial progress is pinned both ways. `--tx-mode all` decides on transactional DDL and carries neither checks nor timeouts. |
| Verb `migrate ls` | ✅ | ❌ | ✅ | `ptah migrations ls` lists a migration directory with no database; `ptah-compat migrate ls` is the drop-in spelling (`--dir`, `-s`, `-l`). Beyond the CE pin, so strict compatibility omits it. |
| Verb `migrate show` | ✅ | ❌ | ✅ | `ptah migrations show` prints a stored migration's SQL with no database; `ptah-compat migrate show {name \| version}...` is the drop-in spelling. Beyond the CE pin, so strict compatibility omits it. |

</div>

## Linting and safety

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| `migrate lint` requires `--latest` or `--git-base` | ✅ | ✅ | ✅ | Refused before the directory is read and before `--dev-url` is contacted. `PTAH_ATLAS_LINT_ALL_VERSIONS=1` restores linting the whole directory here; native `ptah migrations lint` needs no scope. |
| `schema apply --skip-lint` | ✅ | ❌ | ✅ | With an atlas.hcl `lint` policy, the planned SQL is linted against the rules it names and an error-rated finding refuses the apply; `--skip-lint` applies anyway. No policy, no lint pass, as in CE. |
| Analyzers that need a dev-database schema diff | ✅ | ✅ | ✅ | Rules declare whether they read migration SQL or the replayed dev schema; the versions read come from that declaration, and a rule that asks and gets nothing says so on stderr. |
| Apply-time destructive-change gate | ✅ | ❌ | ➖ | migrations up refuses destructive pending files; .ptah-lint.yaml disabled-rules reopens the gate and ptah.sum does not hash that file. |
| Atlas Pro analyzer code coverage | ✅ | ➖ | ✅ | Measured against a reviewed snapshot of the analyzer page rather than the page itself; the generated lint-rules reference carries the verdict for every code in it. |
| Atlas web reports (`--web`) | 🔷 | ❌ | ✅ | `schema inspect --web` and `schema diff --web` write a self-contained HTML ERD locally and open it; nothing is published. `migrate lint --web` stays unregistered. |
| Check bypass on the compat surface | ✅ | ❌ | ❌ | No Atlas build registers `--skip-checks` on migrate apply, so the compat bypass is PTAH_SKIP_CHECKS. On migrate down the flag is registered and reads the same variable. |
| CI integration (GitHub Action, annotations) | ✅ | 🟡 | ✅ | stokaro/ptah-action@v1 posts a sticky PR comment; `--format` github-actions emits annotations. The community binary has no annotation mode; its lint `--format` takes a Go template only. |
| Custom lint rules and check-level policy | ✅ | ❌ | ✅ | A rule is declared in `.ptah-lint.yaml` or an atlas.hcl `rule` block as an expression over the statement; a `naming` block on either surface drives the NM rules. review, non_linear, force do nothing. |
| Default-firing Atlas analyzer concern mapping | ✅ | ➖ | ➖ | lint-analyzer-catalog maps every default-firing Atlas concern to a covering Ptah rule, severity and line; 0 gap on the committed corpus. |
| Dev-URL schema scope on `migrate lint` | ✅ | ✅ | ✅ | `ptah-compat migrate lint` reviews only the schema the dev URL's search_path names, matching the pinned CE binary. Native `ptah migrations lint` reads SQL text and deliberately does not scope. |
| Generation-time destructive-change gate | ✅ | ❌ | ❌ | migrations generate and plan fail with `--check-destructive` when the generated SQL contains destructive statements; `--allow-destructive` reopens the gate. Distinct from the apply-time gate row. |
| Inline nolint suppression | ✅ | ✅ | ✅ | Every code the compat surface prints is silenced by that code; analyzer names work on both surfaces; a blank line detaches a directive. Unknown selectors accepted silently, matching CE. |
| Lint enforcement per surface | ✅ | 🟡 | ✅ | The lint guide's enforcement table says, per surface, which rules run and whether a finding is reported, exits non-zero at a threshold, or refuses the apply; a `gate` section widens the apply gate. |
| Native migration lint rule set | ✅ | 🟡 | ✅ | Every rule is on the generated lint-rules page, grouped by dialect and gated by `--dialect`. Atlas lists destructive and backward-incompatible rules Open; concurrent-index rules Pro. |
| Per-rule severity policy | ✅ | ❌ | 🟡 | Severity vocabulary is info\|warning\|error; only error gates. The community binary carries no severity attribute: it accepts one and ignores it, exactly as it treats an invented attribute. |
| Pre-migration assertion checks | ✅ | ❌ | ✅ | Scalar SELECTs in either direction; txtar checks.sql and checks/*.sql support all-of/oneof groups. One failure mode, abort, and no checks under `--tx-mode all`. |
| SARIF 2.1.0 lint report | ✅ | ❌ | ➖ | Native `--format` sarif emits SARIF 2.1.0 with ruleId, level and file:line; Atlas documents Go-template `--format` output for migrate lint. |
| Schema security findings over roles, grants and policies | 🔷 | ❌ | ✅ | Findings over a live schema's grants, policies, routines and role graph: code, severity, structured detail and a suggestion, local and with no account. Skipped rules are named rather than silent. |
| Standalone SQL file linting (`ptah sql lint`) | ✅ | ❌ | ❌ | Lints arbitrary SQL files or stdin against per-dialect capability presets (9 dialects incl. sqlserver), refined by a `--server-version` server string; text/json output, rule disable. |
| Statement safety classification report | ✅ | ➖ | ➖ | plan `--report` text\|html\|json and generate `--report` html\|json emit highest severity, a destructive flag, and per-statement assessments. |

</div>

## Testing

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| Atlas `.test.hcl` ingestion | ✅ | ❌ | ✅ | Implemented: `.test.hcl` runs beside YAML on `schema test`, `migrate test` and `schema plan test`, with `for_each`, `var`, `self`, `catch`, `assert`, `log`, cleanup, gated `external` and `parallel`. |
| Atlas CE strict oracle profile | ✅ | ➖ | ➖ | Strict mode builds the CE tree and refuses unsafe sources, migration extensions, and catalog-only live objects before output, comparison, or mutation. Default retains the full surface. |
| Atlas-shaped migrate test / schema test verbs | ✅ | ❌ | ✅ | Reports and seed directories are exposed. schema test accepts `-s/--schema`, Go, SQL/HCL, database or `env://` sources; `--var` reaches HCL files. |
| Dev / shadow database verification | ✅ | ✅ | ✅ | `--shadow-db` on generate, checkpoint, baseline and down; `schema apply --dry-run` runs the real rehearsal. All four resolve a `docker://` shadow value. |
| Embeddable test runner (Go package) | ✅ | ❔ | ❌ | migration/dbtest exports RunMigrationTest and RunSchemaTest. CE stays unknown: a CLI probe cannot see a Go API; an Atlas-side source naming a test-runner entry point would settle it. |
| Exit-code contract for CI gates | ✅ | ✅ | ➖ | Native 0/1/2 separates expected negative results from command errors; ptah-compat collapses to Atlas CE 0/1, recovered panics still exit 2. |
| Migration test framework (`ptah migrations test`) | ✅ | ❌ | ✅ | Declarative YAML cases: migrate_to, apply_schema, seed, exec, assert. Fresh ephemeral SQLite per case unless `--db-url` is set. |
| Schema test framework (`ptah schema test`) | ✅ | ❌ | ✅ | Desired schema from Go annotations, SQL or HCL files, or a live database converges before steps. HCL sources take repeatable `--var`; migrate_to is rejected. |

</div>

## Configuration and dev databases

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| `--var` does not require an `atlas.hcl` | ✅ | ✅ | ✅ | `-c` and `--env` select a project file and still require one. `--var` only supplies values to one, on every verb. Its syntax is still checked with no `atlas.hcl` present. |
| A malformed `--var` is refused wherever it is spelled | ✅ | ✅ | ✅ | CE parses `--var` while parsing flags, so a value with no `=` is refused before any project file is sought. Ptah checks it on every command under `schema` and `migrate`, even ones that never read it. |
| Atlas project config (atlas.hcl) | ✅ | ✅ | ✅ | Envs, variables, locals, data sources and `for_each` are evaluated. A collection env expands where a verb takes one and is refused by name elsewhere. |
| atlas.hcl file() and fileset() path confinement | ✅ | ❌ | ❌ | Ptah confines file() and fileset() to the atlas.hcl directory: absolute, parent-traversal and symlink escapes are refused by name. Atlas reads them. Deliberate divergence; exit 1 either way today. |
| atlas.hcl from the native ptah binary | ✅ | ➖ | ➖ | ptah `--env` reads ./atlas.hcl, `--config` names one anywhere else when the path ends in .hcl, and `--var name=value` supplies a variable with no default on every env-aware verb. |
| AWS RDS token project data source (data "aws_rds_token") | ✅ | ✅ | ✅ | Resolves to a SigV4 `rds-db` connect token, matching the pinned community binary parameter for parameter. `endpoint` and `username` required; `region` and `profile` optional. |
| data "hcl_schema" reference | ✅ | ✅ | ✅ | Takes path, paths and vars, and exports .url. `vars` is scoped to the files that data source selects and `--var` does not cross that boundary, as on CE. A bad path or scheme names its rule. |
| Docker dev databases (`docker://` `--dev-url`) | ✅ | ✅ | ✅ | Every verb taking a dev or shadow URL provisions and removes a container. `schema plan` starts none on purpose: a saved plan reads local files and has nothing to replay. |
| env:// desired-state references | ✅ | ✅ | ✅ | Resolves on `--to`/`--from`/`--url`, on `--schema-file` and on `schema test`'s `-u` when a run selects an env. Refusing on `--exclude`/`--include` is deliberate. |
| External program project data source (data "external") | ✅ | ✅ | ✅ | Runs argv directly without a shell and returns untrimmed stdout. Caller cancellation, a 60-second timeout, bounded output, process-tree termination, and sanitized errors define the boundary. |
| GCP Cloud SQL token project data source (data "gcp_cloudsql_token") | ✅ | ✅ | ✅ | Resolves to an OAuth2 access token from application default credentials under the sqlservice.admin scope. The block takes no required attribute. |
| Native project config (ptah.yaml) | ✅ | ➖ | ➖ | Keys url, dev, schemas, exclude, external_schema, migration, lint, migrate, diff, online_ddl. No variables or functions. An unknown key fails, naming the key, its line, and the accepted keys. |
| PTAH_* environment-variable flag equivalents | ✅ | ❌ | ❌ | Every flag on every native verb has a documented PTAH_* environment variable ([env: PTAH_X] in help) except the `--auto-approve` flags, which stay explicit-only. CE annotates no flag with one. |
| Remote directory project data source (data "remote_dir") | ✅ | ❌ | ✅ | Resolves through Ptah's OCI backend: `name`, `tag` and `version` map onto a movable or a write-once tag in the namespace `PTAH_ATLAS_REGISTRY` names. |
| Remote schema project data source (data "remote_schema") | ✅ | ❌ | ✅ | `data "remote_schema"` resolves through Ptah's OCI backend via an internal marker. The `oci://` spelling stays refused on compat flags, where the community binary answers `unknown driver`. |
| Runtime variable project data source (data "runtimevar") | ✅ | ✅ | ✅ | Reads Go CDK runtime-variable URLs with byte-preserving string output and a configurable positive timeout. Constant, file, HTTP(S), AWS, and Google Cloud providers are registered. |
| SQL project data source (data "sql") | ✅ | ✅ | ✅ | Runs one query and exports count, first value, and all values. Requires one column and one HCL row type. Heterogeneous rows fail explicitly; Atlas CE panics. Unreferenced blocks stay lazy. |
| Template directory project data source (data "template_dir") | ✅ | ✅ | ✅ | Shared Go templates emit root lowercase .sql migrations only. New and diff synchronize new files and checksum to the confined source path; hash-only stays virtual. |
| Variables, locals, and HCL functions | ✅ | ✅ | ✅ | Variables, locals and data sources are evaluated in both files, against one function set: the project evaluator shares the schema evaluator's, overlaying the three names bound to its own directory. |

</div>

## Databases and schema objects

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| Capability profile of a live target (`ptah db capabilities`) | ✅ | ❔ | ❌ | Reports the dialect, resolved preset and how it was reached, the support level and every capability key for a connected server, as text or stable JSON. No source here names an Atlas equivalent. |
| ClickHouse (clickhouse, ch) | ✅ | ❌ | ✅ | Tables, indexes, plain views, materialized views including the refreshable form, roles, grants, row policies and named table CHECKs. Other kinds are named on render and on the plan path. |
| CockroachDB (cockroachdb, crdb) | ✅ | ❌ | ✅ | `CONCURRENTLY`, XML, advisory locks and DEFERRABLE are engine limits; 25.4 also refuses generic DROP CONSTRAINT, and 26.2 accepts it. Row-level TTL is fully managed. |
| Declared support level per database release line | ✅ | ❔ | ❔ | Every declared release line carries a support level, counted in the generated table the evidence names. End-of-life lowers the level, not the behavior; an undeclared line resolves to best-effort. |
| Domains, composite types, and range types | ✅ | ❌ | ✅ | Domains and composites compare and change in place, each kind gated per target. Range types rebuild because PostgreSQL offers no ALTER TYPE ... AS RANGE. |
| Enum types | ✅ | ✅ | ✅ | An enum is whatever the schema declares as one. PostgreSQL alters values in place; MySQL, MariaDB, SQLite and SQL Server carry them in the column type or a check constraint. |
| Extensions | ✅ | ❌ | ✅ | PostgreSQL and YugabyteDB create, compare and relocate extensions, version included. Every other target names the declaration it cannot host rather than dropping it. |
| Functions | ✅ | ❌ | ✅ | Functions and procedures render, read and plan on the PostgreSQL and MySQL families and SQL Server. ClickHouse and SQLite host neither. |
| MySQL and MariaDB | ✅ | ✅ | ✅ | Roles, grants, stored functions and MariaDB sequences render, read back and plan. An inline enum change plans a real MODIFY COLUMN and converges; matviews fail closed. |
| Oracle, Snowflake, Redshift, Databricks | 🟡 | ❌ | ✅ | Oracle renders, plans, reads and converges; Snowflake, Redshift and Databricks have no endpoint anyone can measure and are out of scope with the reason (stokaro/ptah#1875). |
| PostgreSQL 12+ (postgres, postgresql) | ✅ | ✅ | ✅ | Reference engine of the PostgreSQL family: views, matviews, functions, triggers, sequences, roles, RLS and domains all render. Presets 12-13, 14-16, 17+ from the server banner. |
| Roles, grants, and row-level security | ✅ | ❌ | ✅ | Roles and grants wherever the `role_management` key is true, which the preset matrix names. Row-level security on the PostgreSQL family, SQL Server and ClickHouse; the rest have no such object. |
| Spanner PostgreSQL interface (spanner) | ✅ | ❌ | ✅ | Enums, matviews, functions and triggers render as named skips; sequences render with the start counter only, foreign keys render, SERIAL errors. Probed live, with a compose service and a suite. |
| SQL Server and Azure SQL (sqlserver, mssql, tsql) | ✅ | ❌ | ✅ | Every spelling renders the same DDL. Tables, views, triggers, synonyms, sequences, roles/grants, row-level security, functions and extended properties all render, read back and plan. |
| SQLite (sqlite, sqlite3) | ✅ | ✅ | ✅ | Column drops, type, nullability, default, generated, table-constraint and add-column changes all rebuild, inbound foreign keys included. The engine has no other object kind Ptah models. |
| Standalone sequences | ✅ | ❌ | ✅ | Every target with standalone sequences emits, reads and plans them, Spanner included. MySQL, ClickHouse and SQLite have none and name the omission (stokaro/ptah#1856). |
| TiDB and LibSQL | ✅ | ✅ | ✅ | TiDB is reached through `mysql://`, which both binaries do and neither exposes as its own driver. `libsql://` and `libsql+ws://` resolve onto the SQLite dialect over the remote transport. |
| Triggers | ✅ | ❌ | ✅ | Every engine spelling renders the same trigger DDL and four readers read them back. ClickHouse and Spanner have none and name the omission; MySQL statement triggers and SQL Server BEFORE do not exist. |
| Views and materialized views | ✅ | ❌ | ✅ | Plain views work everywhere. Materialized views render on PostgreSQL, CockroachDB, YugabyteDB and ClickHouse, whose scheduled form carries its REFRESH EVERY\|AFTER clause. |
| YugabyteDB (yugabytedb, ysql) | ✅ | ❌ | ✅ | Roles, grants, RLS, sequences, domains, views, matviews, functions, triggers and CREATE INDEX CONCURRENTLY are enabled. Three keys stay off because the server refuses them, not Ptah. |

</div>

## Go embedding and developer tooling

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| Annotation metadata as JSON Schema | ✅ | ➖ | ➖ | Emits a JSON Schema describing every //ptah directive and attribute; Atlas has no //ptah annotation set for the concept to apply to. |
| API schema export: OpenAPI 3.0 and GraphQL | ✅ | ❌ | ❌ | Exports from Go, YAML, HCL, SQL, or DBML. Go, YAML, and HCL author metadata; SQL and DBML derive public names from storage. No handlers or resolvers. Absent from CE inventory and cited Pro list. |
| Concurrency-guarded migration plan publication | ✅ | ➖ | ➖ | generator.PlanMigration binds a plan to a directory snapshot; WriteFiles rejects changed history (ErrMigrationDirectoryChanged) under a cross-process lock; concurrent reuse fails (ErrMigrationPlanInUs |
| Go annotations to HCL export with cleanup | ✅ | ➖ | ➖ | Writes HCL from Go annotations; cleanup requires zero diagnostics and refuses unparsed directives. Opaque function, view, materialized-view, and trigger bodies are reported and block cleanup. |
| Pinned database sessions (WithSession) | ✅ | ➖ | ➖ | `WithSession` on `DatabaseConnection` pins one physical session for a callback, rebinding reader/writer/SQL runner, and discards the connection so session state cannot leak. |
| Protobuf schema export with pinned field numbers | ✅ | ❌ | ❌ | Edition 2023 from Go, YAML, HCL, SQL or DBML. Go, YAML and HCL author export metadata; SQL and DBML carry storage only. `--out` pins field numbers and compatibility policies. Not in CE inventory. |
| ptah-ls annotation language server | ✅ | ➖ | ➖ | stdio LSP over //ptah annotations: hover, completion, diagnostics, plus a VS Code extension. Tied to Ptah's own annotation syntax. |
| Public API compatibility gate | ✅ | ➖ | ➖ | check-public-api.sh keeps the committed API baseline and the package tree in sync; pre-v1 breaks need a per-baseline approval line. |
| Query builder for parameterized SQL | ✅ | ➖ | ➖ | Joins, DISTINCT, GROUP BY, HAVING, RETURNING, LIKE, upsert, CTEs, subqueries, arithmetic, function calls, INSERT … SELECT and window functions all render. No frame clause yet. |
| Reusable Go packages (embedder API) | ✅ | ➖ | ➖ | Documented embedder packages cover parse, diff, plan, render, migrate, lint and seed. CE conformance measures CLI commands, not Go APIs. |
| Schema visualization (ERD diagrams) | ✅ | ❌ | ✅ | Mermaid, DOT or SVG ERD from Go annotations; SVG shells out to Graphviz dot. `schema export --to html` and the compat `--web` write a self-contained HTML ERD. |
| Statement observer and validator hooks (Go API) | ✅ | ➖ | ➖ | migrator.WithStatementObserver runs a read-only callback per executed statement; WithStatementValidator gates all statements pre-execution; both compose with StatementInterceptor. |
| testkit companion module for database tests | ✅ | ➖ | ➖ | Separate repository and module ptah.run/testkit wraps testcontainers-go for tests needing real databases; versions independently and stays out of the main module graph. |

</div>

## Data and distribution

Ptah's registry story differs from Atlas's in storage, not in function.
Everything artifact distribution needs — publish a migration directory or a
desired schema, pull it elsewhere, pin an exact version, run migrations
straight from the registry — works against any OCI-compliant registry: GHCR,
ECR, GAR, Harbor, Docker Hub, or a self-hosted `registry:2`.

For a team this means the registry and credentials already used for container
images also serve schema artifacts; there is no separate account, login verb,
or hosted service to depend on; and a digest pin makes a deployment
reproducible byte for byte. The artifacts are ordinary OCI 1.1 manifests, so
registry-side controls — replication, retention, immutable-tag policy, access
control — come from the registry, not from Ptah. The full workflow is on
[OCI registry artifacts](../../operate/oci-registry/).

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| Artifact integrity check (`--verify-sum`) | ✅ | ➖ | ❌ | On migrations push (local, pre-upload) and on up/down/status (pulled). A sum checks a directory against the sum stored beside it, so an `oci://` tag proves internal consistency only; pin a digest. |
| Declarative reference data | ✅ | ❌ | ✅ | //ptah:schema:data rows diffed by key into a reversible data migration. Atlas lists declarative data management as a Pro feature. |
| Digest pinning and write-once version tags | ✅ | ➖ | ❌ | Pushing to an @sha256 reference is refused; `--version` is write-once and a conflict exits 2. The reference tag, `--tag` values and latest all move. |
| Environment-scoped SQL seed runner | ✅ | ❌ | ❌ | NNN_desc.env.sql files recorded in schema_seeds with protected-env gates. No seed verb in the CE inventory or the cited Pro list. |
| oci:// as a `--schema-file` desired-state source | ✅ | ❌ | ✅ | Accepted wherever a desired state or a migration directory is read from a registry. Every verb that resolves one exposes `--plain-http`, and a walk of the command tree gates the pairing. |
| Referrer attachments: lint, plan, deployment reports | ✅ | ❌ | ❌ | lint `--attach`, migrations plan `--attach` and up attach reports to an exact digest, and `oci fetch` returns the payload behind a descriptor rather than the descriptor alone. |
| Registry-backed distribution: `oci://` vs `atlas://` | ✅ | ❌ | ✅ | `atlas://` functions — publish, pull, digest-pin, run migrations directly, schemas via `--schema-file` — over any OCI registry, no account. See [OCI registry artifacts](../../operate/oci-registry/). |

</div>

## Atlas Registry and Cloud

These rows are the services Atlas hosts on top of its registry — approvals,
reporting, monitoring, the `atlas://` scheme itself. They concern the hosted
service, not artifact storage; the storage function is covered under
[Data and distribution](#data-and-distribution).

This is where 🔷 and ❌ have to be read apart. 🔷 means the row's job is done
here already, over an ordinary OCI registry and local plan files, and only the
hosted protocol is out of reach — nothing to wait for. ❌ means the capability
is genuinely absent and the difference column names the issue that owns it.

<div class="ptah-wide-table">

| Capability | Ptah | CE | Pro | Difference |
| --- | :-: | :-: | :-: | --- |
| `atlas://` vendor protocol | 🔷 | ❌ | ✅ | Ptah addresses any OCI registry with `oci://` and its own push and pull verbs. The vendor spelling resolves against the namespace `PTAH_ATLAS_REGISTRY` names, on desired-state flags and atlas.hcl. |
| `migrate push` and `schema push` | 🔷 | ❌ | ✅ | `ptah schema push` and `ptah migrations push` publish to any OCI registry. The Atlas verbs address the hosted registry an account owns; the compat stubs say so and exit 1. |
| `schema plan --edit` and `--name-format` | ✅ | ❌ | ✅ | `--edit` preserves comments and re-derives dialect-aware severity; `--name-format` uses Atlas-shaped Base64 .FromHash/.ToHash values. |
| `schema plan --format` and `--directive` | 🔷 | ❌ | ✅ | Both are implemented under Ptah's own payload and directive vocabulary. Atlas gates the verb behind its hosted registry, so its field names and directive set are not obtainable. |
| `schema plan --push`, `--pending`, `--repo` | 🔷 | ❌ | ✅ | All three name a repository in the hosted registry and fail loudly. Ptah's plan workflow saves and reads local plan files instead, so the function is here and the service is not. |
| `schema plan --skip-lint` | ✅ | ❌ | ✅ | Accepted, and does nothing: `schema plan` runs no lint step, so there is nothing to skip. A Pro pipeline passing it keeps working; no check is loosened. |
| `schema plan lint` | ✅ | ❌ | ✅ | Implemented: the plan is verified against the transition, then Ptah's lint rules report on its SQL. Findings do not change the exit code; an opt-in variable makes an error-severity finding exit 1. |
| `schema plan new` and `schema plan validate` | ✅ | ❌ | ✅ | Implemented. Flag sets match the documented Atlas help; runtime parity remains unverified. Successful Ptah runs keep stderr free of development notes. |
| `schema plan test` | ✅ | ❌ | ✅ | Runs `test "plan"` cases: establish a state, verify the plan was computed for it, apply the plan file, assert. Local by its flag set, and now by its implementation. |
| `schema plan` registry sub-verbs (approve, list, pull, push, rm) | 🔷 | ❌ | ✅ | These five arbitrate plan state inside the hosted registry. Ptah keeps plan state in local plan files that the ordinary `schema plan` verbs read and write; the service is out of reach. |
| Atlas Cloud deployment reporting | 🔷 | ❌ | ✅ | Ptah attaches a deployment-report referrer to its own OCI artifact after an `oci://` migrations up, so the report is readable from the registry. There is no account model to report into. |
| Atlas Copilot (AI assistant) | 🔷 | ❌ | ✅ | `ptah mcp` gives any AI client Ptah's schema operations over the Model Context Protocol, locally, plus a gated write surface for migration, schema and test files (stokaro/ptah#1487). |
| Column-level data lineage | ✅ | ❌ | ✅ | `ptah schema lineage` derives column-to-column edges from view and materialized-view bodies and emits them as a table or JSON. A body it cannot resolve is reported, not omitted. |
| Hosted Schema Docs (schema documentation) | 🔷 | ❌ | ✅ | `ptah schema export --to markdown` writes reference documentation locally: a section per table with columns, types, defaults, keys, comments, indexes and enums. Not a hosted service. |
| Reviewer approval and policy workflows | ✅ | ❌ | ✅ | `ptah schema approve` signs a plan with an SSH key; `--require-approval` refuses to apply one that does not verify against a committed allowed_signers file. No identity service. |
| Schema monitoring, hosted UI, login | 🔷 | ❌ | ✅ | `ptah schema serve` shows drift live and `schema export --to html` writes the same view as one file. No login, and none intended (stokaro/ptah#1857). |

</div>

## How these rows were established

The Ptah column is derived from the built binaries. `ptah --help` and
`ptah-compat --help` are the strongest available proof that a capability
exists, and a row that could not be demonstrated that way is marked 🟡 with the
limitation named, or ❌.

🔷 is the one verdict `--help` cannot settle on its own. It is claimed only
where both of these hold: the capability is demonstrable on a Ptah
binary in a Ptah spelling, and the remainder is access to a service another
company hosts behind its own accounts. Both halves are stated in the row's
difference column, so the claim can be argued with. Where only the first holds,
the row is ✅; where only the second does, it is ❌ with an issue.

The Atlas columns are narrower on purpose. Ptah is a clean-room implementation
that studies observable behavior only, so the Atlas CE column is derived from
the command, usage, and flag inventory the conformance harness reads out of the
pinned Atlas community binary, and the Pro/Cloud column from the classification
Atlas publishes on its feature availability page. Where neither source settles
a question, the row says so rather than guessing.

The full per-row evidence — the command that was run or the source cited for
every cell — is version-controlled at
`docs/site/scripts/data/feature-matrix-rows.json`, so a row can be re-verified
or disputed without archaeology.

Four sources carry most of the weight:

- [`cli-surface.md`](https://github.com/stokaro/ptah-atlas-conformance/blob/main/cli-surface.md)
  inventories every command in Atlas CE v1.3.0 and classifies it as an OSS
  parity target or out of scope, with the reason recorded per command.
- [`ce-gating.md`](https://github.com/stokaro/ptah-atlas-conformance/blob/main/ce-gating.md)
  goes further than the inventory: it runs the pinned CE binary
  through the capability set this page asserts about the CE column and records
  the observed class per scenario — works, community-abort stub, absent verb,
  unknown flag, or silently unenforced. A version bump that changes Atlas's
  gating turns that gate red.
- [`gaps.md`](https://github.com/stokaro/ptah-atlas-conformance/blob/main/gaps.md),
  [`gaps-live.md`](https://github.com/stokaro/ptah-atlas-conformance/blob/main/gaps-live.md),
  and [`gaps-diff.md`](https://github.com/stokaro/ptah-atlas-conformance/blob/main/gaps-diff.md)
  record measured outcomes over Atlas fixtures, live databases, and Atlas CE
  differential checks.
- [`docs-surface.md`](https://github.com/stokaro/ptah-atlas-conformance/blob/main/docs-surface.md)
  indexes the full atlasgo.io documentation universe — 351 pages from the
  site's own sitemap — into a triage registry, so parity is built against the
  whole documented Atlas surface rather than a hand-picked subset. The registry
  starts mostly untriaged and its budget ratchets down as pages are worked
  through
  ([campaign](https://github.com/stokaro/ptah-atlas-conformance/issues/239)); a
  weekly job re-fetches the sitemap so new or renamed Atlas docs pages surface
  as red rather than silently missing from this page.

## What this page does not claim

A green conformance run is a floor on the distance to Atlas, never a ceiling.
The conformance repository states it directly: no number it produces is a
full feature-set parity test, and several runtime dimensions stay unmeasured.

Specifically, this page does not claim that Ptah reproduces Atlas byte for
byte, that a ✅ row behaves identically under every flag combination, or that
the Atlas columns are exhaustive for capabilities Atlas ships outside its
documented CLI surface.

## Next steps

- Differences Ptah keeps on purpose, each with its measurement:
  [Compatibility differences](../retained-divergences/).
- The measured evidence and how to re-run it: [Conformance](../conformance/).
- Which Atlas documentation area maps where: [Atlas docs coverage](../docs-coverage/).
- Why Ptah can be Atlas-compatible without Atlas code:
  [License boundary](../license-boundary/).
