# `mgf-alembic` — async-aware alembic env.py helper for mgf-common consumers

[![Registry](https://img.shields.io/badge/registry-Codeberg-blue)](https://codeberg.org/magogi-admin/mgf-alembic)
[![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://codeberg.org/magogi-admin/mgf-alembic)

> **Shape:** Federation sibling of `mgf-common`. The current
> `mgf-common` pin window is shown in this package's
> [Codeberg registry](https://codeberg.org/magogi-admin/mgf-alembic) —
> sourced from `pyproject.toml` at release time, so this README
> can't go stale. (Per v2.6 DOC-01 / project-shape taxonomy.
> PAPER-38.)
>
> **Sibling of [`mgf-common`](https://codeberg.org/magogi-admin/mgf-common) +
> [`mgf-sqlalchemy`](https://codeberg.org/magogi-admin/mgf-sqlalchemy) under
> the `mgf.*` namespace.** Houses the async-aware alembic `env.py`
> helper that previously lived under `mgf.common.alembic.*` —
> extracted at mgf-common v0.30 / mgf-alembic v0.1 (paired with
> mgf-sqlalchemy v0.1) per the
> [federation split plan](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/release/federation_roadmap.md).

> **Scope — DB-schema migrations only.** `mgf-alembic` wraps alembic
> for **SQL database schemas**. It is NOT for typed file-store
> versioning or YAML/JSON config migrations — for "a typed file with
> a `version:` field that needs migrating from N to N+1", use
> [`mgf.common.versioned.VersionedFileStore[T]`](https://codeberg.org/magogi-admin/mgf-common)
> instead. (Scope delineation per vm-vmanager-app's 2026-05-18
> meta-feedback; see `FEEDBACK.md` §3.)

## What this provides

| Submodule | What |
|---|---|
| `mgf.alembic` | `configure_env` — one call replaces the ~40-line `env.py` boilerplate. Auto-detects sync vs async drivers from the URL prefix (`postgresql+asyncpg://`, `sqlite+aiosqlite://`, etc.). Plumbs `compare_type`, `compare_server_default`, `naming_convention`, `include_object`, `include_schemas` through to `alembic.context.configure`. |

`include_object` / `include_schemas` were added per
[PAPER-22](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md)
in mgf-common v0.19; carried over to mgf-alembic v0.1 unchanged.

## Install

```bash
pip install mgf-alembic
# Or with the test extra (aiosqlite for async-driver round-trip tests):
pip install 'mgf-alembic[test]'
```

Pulls in `mgf-common`, `mgf-sqlalchemy`, and `alembic>=1.13`
automatically.

## Quick start — the entire env.py

```python
# alembic/env.py
from mgf.alembic import configure_env
from myapp.config import MyAppSettings
from myapp.db.base import Base

settings = MyAppSettings()
configure_env(
    database_url=settings.database_url,
    target_metadata=Base.metadata,
)
```

That's the entire file. The helper detects offline vs online mode,
runs the migration, disposes the engine cleanly. Async drivers
(asyncpg, aiomysql, aiosqlite, asyncmy, async psycopg) are
auto-detected; sync drivers fall through the synchronous path.

## PostGIS / TimescaleDB: filter extension tables

```python
def _exclude_extensions(obj, name, type_, reflected, compare_to):
    return not (type_ == "table" and name in {"spatial_ref_sys"})

configure_env(
    database_url=settings.database_url,
    target_metadata=Base.metadata,
    include_object=_exclude_extensions,
)
```

`alembic revision --autogenerate` no longer proposes phantom drops
for extension-managed tables (PostGIS `spatial_ref_sys`,
TimescaleDB `_timescaledb_internal`, pgvector housekeeping).

## Naming-convention shape

Constraint names auto-generated by `op.create_*` are otherwise
anonymous and vary across DBs — pin them:

```python
configure_env(
    database_url=settings.database_url,
    target_metadata=Base.metadata,
    naming_convention={
        "ix": "ix_%(column_0_label)s",
        "uq": "uq_%(table_name)s_%(column_0_name)s",
        "ck": "ck_%(table_name)s_%(constraint_name)s",
        "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
        "pk": "pk_%(table_name)s",
    },
)
```

## Documentation

- [`docs/recipes/alembic.md`](docs/recipes/alembic.md) — full async-alembic walkthrough.
- [`docs/cutover/v0.1.0.md`](docs/cutover/v0.1.0.md) — maiden voyage migration story (the v0.30 split).
- [`PUBLIC_API.md`](PUBLIC_API.md) — full public surface contract.
- [`CHANGELOG.md`](CHANGELOG.md) — release history.

For the federation-wide engineering standards (DESIGN_PRINCIPLES,
ERROR_HANDLING, SECURITY, etc.) see
[`mgf-standard/docs/standards/`](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/).
This sibling inherits them by reference; the standards
source-of-truth lives in mgf-common.

> **Conformance: L2** per [`mgf-standard/docs/standards/`](https://codeberg.org/magogi-admin/mgf-standard/src/branch/main/docs/standards/). Per-rule audit ledger: [`docs/inprogress/MGF_STANDARDS_CONFORMANCE.md`](docs/inprogress/MGF_STANDARDS_CONFORMANCE.md). L2 means every federation **MUST** rule applies, evidenced by the tracker.

## Status

🚧 **Experimental** — every public name is `experimental` per AP-09.
Promotion to `stable` happens release-by-release as consumer feedback
in [`mgf-common/FEEDBACK.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md)
converges. The 0.x window applies. Pin tightly:
`mgf-alembic = ">=0.X.0,<0.Y"`.

## Cross-references

- **Filing process for sharp edges**: open an entry on
  [`mgf-common/FEEDBACK.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/FEEDBACK.md)
  with `[mgf-alembic]` prefix, OR file directly on this repo's Issues
  → maintainer mirrors into the canonical FEEDBACK.md.
- **Federation pattern**:
  [`mgf-common/docs/design/federation.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/design/federation.md).
- **The split that created this sibling**:
  [`mgf-common/docs/release/federation_roadmap.md`](https://codeberg.org/magogi-admin/mgf-common/src/branch/main/docs/release/federation_roadmap.md).
- **Companion sibling**: [`mgf-sqlalchemy`](https://codeberg.org/magogi-admin/mgf-sqlalchemy) — async SQLAlchemy engine + sessionmaker + tenant-scoping (paired ship at v0.30; this sibling depends on it).
