# `Arcana.Graph.Migration`
[🔗](https://github.com/georgeguimaraes/arcana/blob/main/lib/arcana/graph/migration.ex#L1)

Versioned migrations for Arcana's GraphRAG tables.

GraphRAG is optional, so it carries its own version separate from
`Arcana.Migration`. Install it the same way:

    defmodule MyApp.Repo.Migrations.AddArcanaGraph do
      use Ecto.Migration

      def up, do: Arcana.Graph.Migration.up(dimensions: 384)
      def down, do: Arcana.Graph.Migration.down()
    end

Upgrade by adding another host migration that calls `up/1`. Version 2 adds
relationship provenance DDL and clears legacy relationship facts that have
no source chunk.

## Options

  * `:version` - target version (defaults to the latest)
  * `:dimensions` - embedding dimensions for `arcana_graph_entities.embedding`.
    Must match `Arcana.Embedder.dimensions/1` for the embedder you run
  * `:prefix` - Postgres schema to install into (defaults to the
    connection's current schema)
  * `:create_schema` - whether to create `:prefix` when it is missing
    (defaults to `true`, ignored without a prefix)

## Adoption

Installs predating this module have graph tables but no recorded version.
Version 1 converges them: it creates only what is absent and adds only the
columns and indexes a later release introduced. The one thing it drops is a
unique index whose shape is wrong: see "What converge verifies" below.

## Where the version is recorded

The applied version is stored as the Postgres comment on `arcana_graph_entities`, as
exactly `arcana_graph:<n>`:

    SELECT obj_description('arcana_graph_entities'::regclass);

Arcana owns that comment. Two consequences worth knowing before you point
schema-documentation or data-catalog tooling at this table:

  * recording a version replaces whatever comment is there, so a
    description you wrote on `arcana_graph_entities` is lost on the next migration
  * a comment Arcana doesn't recognise reads as version 0, the same as a
    fresh install, so `up/1` re-runs the converge path (idempotent, and
    harmless) while `down/1` declines to drop anything

Comment on any other table freely. Only `arcana_graph_entities` is reserved.

## What converge verifies

Adoption creates only what is absent, so most objects are checked by name
alone. Three are verified against the catalog and corrected when they
differ, because being merely present isn't enough:

  * `arcana_documents.collection_id`'s delete rule, since older templates
    emitted `ON DELETE SET NULL`
  * the embedding column's dimension, which is compared against
    `:dimensions` rather than altered
  * the unique indexes on `arcana_graph_entities(name, collection_id)` and `arcana_graph_entity_mentions(entity_id, chunk_id)`, since `create_if_not_exists` matches on the index name and an
    older template may have created one with a different shape

Everything else is create-if-absent. For the plain indexes that is
deliberate: a differing one costs performance, not correctness.

## Version history

  * 1 - entities, relationships, mentions and communities, including the
    entity-mention unique index and `communities.summary_fingerprint` that
    earlier releases shipped as separate upgrade migrations
  * 2 - canonical relationship facts with per-chunk evidence. Existing
    relationships are cleared because their source chunks cannot be inferred;
    rebuild the graph after upgrading

## Uninstall ownership

Target version 0 removes only Arcana's graph tables and table-contained
objects. It leaves the core tables, Postgres schema, and `vector` extension in
place. Foreign keys, views, and materialized views owned by the host
application block uninstall with their schema-qualified identities. Arcana
never uses `DROP ... CASCADE`, because that would delete host-owned objects.

# `current_version`

The latest graph version this release of Arcana knows how to migrate to.

# `down`

Migrates down to `:version`, or removes the graph tables entirely after
checking for host-owned dependents.

# `recorded_version`

The graph version recorded in the database, or 0 when GraphRAG has never
been installed.

Pass a repo when calling this outside a migration; inside one it defaults
to the migration's own repo. Pass `:prefix` to read a version recorded in
a Postgres schema other than the current one.

Both arguments default, so options must be given with a repo:
`recorded_version(MyApp.Repo, prefix: "tenant_a")`. A lone keyword list
would bind to the repo argument.

# `up`

Migrates up to `:version`, or to the latest version.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
