# Akashi migration handover examples

The article uses Ptah Compat 0.11.4, Atlas 1.1.0, and PostgreSQL 18.6 with
TimescaleDB 2.30.2 and pgvector 0.8.1. Use disposable databases only.

## Source and license

`akashi/` contains the 91 migration SQL files, original `atlas.sum`, `atlas.hcl`,
Makefile, and exit-criteria checker from the proposal's frozen source revision.
Their bytes are unchanged. The Makefile is stored as `Makefile.txt` so the blog
can serve it as a text download; copy it to `Makefile` before using the targets. `AKASHI-LICENSE.txt` is the upstream Apache 2.0 license.
The proposal changes only a CI workflow and the runbook; article fixtures outside
`akashi/` are isolated reproductions and are not proposed production migrations.

The upstream PR still pins Ptah 0.8.1. This article uses the separately released
0.11.4 binary. `verified.json` records source commits, binary and image hashes,
results, and hashes of the supporting files. It does not claim upstream adoption.

## History handover

Install both CLIs, make, Python 3, and psql. Start a disposable database using the
recorded TimescaleDB image, set `DATABASE_URL`, and run these from `akashi/`:

```sh
sh ../commands/bootstrap.txt
sh ../commands/validate.txt
sh ../commands/apply.txt
sh ../commands/status.txt
STRICT_RETENTION_CHECK=false make verify-exit-criteria
sh ../commands/apply.txt
```

Atlas must report version 111, 91 executed files and zero pending files. The
second Ptah apply must have no pending work. For the reverse test, create a
second fresh database, bootstrap extensions, apply using `atlas migrate apply
--env local`, then run `commands/ptah-status.txt` from `akashi/`.

For continuation, copy the migration directory to `next-migrations`, add
`handover-migrations/112_handover_probe.sql`, and regenerate that copy's
checksum file with `atlas migrate hash --dir file://next-migrations`. Apply it
with Atlas after the Ptah history, and with Ptah after the Atlas history. The
other tool must read version 112 with no pending files. Do not alter the
original migration directory for this probe.

`measured/history-commands.json` records each actual invocation, database and
exit code. The original migration files, configuration and checksum file were
SHA-256-compared after the run and matched the supplied source bytes.

## Invalid-index refusal and recovery

Use two new disposable databases, one per tool, and run from this directory.
For each database, execute `sql/index-setup.sql` with psql, then
`sql/index-fail.sql` with `ON_ERROR_STOP=1`. The unique concurrent build must
fail on duplicates and leave an index with both flags false. Execute
`sql/index-deduplicate.sql`, then query `sql/index-state.sql`.

The checked-in `index-migrations/atlas.sum` was generated by
`ptah-compat migrate hash --dir file://index-migrations`. Run
`commands/index-apply.txt` for Ptah, or substitute `atlas` for the same invocation
on the control database. `--allow-dirty` explicitly permits this deliberately
nonempty starting state; it does not waive index validity.

Expected: Ptah exits 1, identifies the unusable index and REINDEX, and records
no successful revision. Atlas exits 0 and records one successful revision while
the index stays invalid. Count successful revisions with both NULL and empty
error values accepted: `applied = total AND COALESCE(error, '') = ''`.

Execute `sql/index-repair.sql` on the Ptah database, then retry the apply. Both
index flags must now be true, and inserting a duplicate email must fail. The
same insert on the Atlas control succeeds while its index remains invalid.
`measured/index-commands.json` records expected failures as well as successes.

## Lock timeout

On another disposable database, execute `sql/lock-setup.sql`. In a separate
session, begin a transaction and acquire `LOCK TABLE lock_probe IN ACCESS
EXCLUSIVE MODE`. Keep that transaction open. Run `commands/lock-apply.txt`:
the migration must fail with SQLSTATE 55P03 under its 2s PostgreSQL lock timeout.
The measured outer command time includes Docker exec overhead.

Release the holder transaction afterward. In the recorded run the holder was
identified by the unique application name `akashi_blog_lock` and terminated
explicitly. The remaining test containers, volumes and images were removed.
This experiment is about PostgreSQL statement locks, not the CLI advisory lock.

## Scope and output

The exit-criteria check ran on the freshly migrated database. Strict retention
and Qdrant reconciliation were disabled, matching the proposal. It is not a
production-load or complete application-test claim.

The capture kept stdout and stderr separate. Text recordings trim trailing
whitespace and final blank lines; `raw_output_sha256` retains the original
stream hashes. The shorter status/error files are excerpts used by the article.
`files_sha256` covers every supporting file except the manifest itself.
The blog's example gate compares each labeled code block to its supporting file.
