# module-migrations ## Purpose Defines per-module migration infrastructure: each module owns its schema via embedded migration files, assembled into a global sequence by the db package. ## Requirements ### Requirement: Dependency-ordered migration execution `RunMigrations` SHALL apply the core stream first, then core-module streams (`domains`), then integration streams. Integration streams SHALL be mutually order-independent, but MAY depend on core and core-module objects (their FKs and grants reference only `core` and `domains`). Each stream SHALL be applied by its own goose run against its own ledger. Execution SHALL remain idempotent. Rollback SHALL undo one step of the most-recently-populated stream in reverse source order (integrations before core-module streams before core); status SHALL report each stream's ledger separately. #### Scenario: Migrations run in dependency order - **WHEN** `RunMigrations` executes on an empty database - **THEN** the core stream SHALL complete before the domains stream begins - **AND** the domains stream SHALL complete before any integration stream begins #### Scenario: Idempotent migration execution - **WHEN** `RunMigrations` executes twice against the same database - **THEN** the second run SHALL apply nothing and SHALL NOT error #### Scenario: Rollback steps integrations back before core - **WHEN** `RollbackMigration` is invoked on a fully migrated database - **THEN** it SHALL roll back one step of the last integration stream with applied migrations, not the core or domains stream ### Requirement: Per-module sqlc configuration Each Go package with queries SHALL keep its own `sqlc.yaml`, with `schema:` paths pointing at the new baseline migration files (plus `internal/db/sqlc_schemas.sql` declaring exactly `core`, `domains`, `stripe`, `fedwiki`). A package whose queries join another module's tables SHALL include that module's migrations in its `schema:` list (the fedwiki store includes the domains migrations). Rename maps SHALL be pinned so that generated Go type names are unchanged by the schema consolidation. #### Scenario: Billing package sqlc config after consolidation - **WHEN** sqlc generates for `internal/billing` - **THEN** queries reference `core.*` tables - **AND** generated type names are identical to their pre-consolidation names #### Scenario: FedWiki sqlc config with schema awareness - **WHEN** sqlc generates for the fedwiki store - **THEN** provider tables resolve in the `fedwiki` schema, core references resolve in `core`, and registry joins resolve in `domains` ### Requirement: Cross-module FK references in migrations use schema-qualified notation When a module migration references a table in another module, the FK constraint SHALL use schema-qualified notation (e.g., `REFERENCES identity.persons(person_id)`) (Decision 114). Intra-module FK references SHALL omit the schema prefix. #### Scenario: Organization migration references identity schema - **WHEN** the organization module's init migration creates a FK to `persons` - **THEN** the FK SHALL use `REFERENCES identity.persons(person_id)` - **AND** intra-module FKs like `REFERENCES organizations(org_id)` SHALL omit the schema prefix #### Scenario: Billing migration references organization and identity schemas - **WHEN** the billing module's migration creates FKs to `organizations` and `persons` - **THEN** the FKs SHALL use `REFERENCES organization.organizations(org_id)` and `REFERENCES identity.persons(person_id)` ### Requirement: Three ordered migration streams Migrations SHALL be organized as the core stream (`internal/db/migrations/`), plus core-module streams registered explicitly in `internal/migrate` (`domains` from `internal/domains/migrations/`), plus one stream per registered integration (`internal/integrations//migrations/`). `migrate.Sources()` SHALL enumerate integration streams from the integration registry, after core and core-module streams. Adding a new integration SHALL add its stream via registration alone; the core stream SHALL always run first, core-module streams next, and integration streams SHALL be mutually order-independent. #### Scenario: Fresh database migrates core first - **WHEN** migrations run against an empty database - **THEN** the core stream SHALL apply before the domains stream and any integration stream - **AND** integration-stream foreign keys and grants into `core` and `domains` SHALL resolve because their targets already exist #### Scenario: Adding a new integration - **WHEN** a new provider integration is registered - **THEN** its migration stream SHALL be picked up from the registry without edits to `internal/migrate` - **AND** existing streams' ledgers and version numbers SHALL be unaffected ### Requirement: Cross-schema FK direction is integration to core only Cross-schema foreign keys SHALL follow the dependency direction only: integration-stream migrations MAY declare foreign keys referencing `core` tables (and MAY reference `domains` objects in grants or data migrations); the domains stream MAY reference `core` only; core-stream migrations SHALL NOT reference any other application schema. #### Scenario: Stripe mapping table references core - **WHEN** a stripe-stream migration creates a mapping table - **THEN** it MAY declare `REFERENCES core.products(product_id)` #### Scenario: Core migration attempts integration reference - **WHEN** a core-stream migration is reviewed - **THEN** any reference to `stripe.*`, `fedwiki.*`, or `domains.*` SHALL be rejected #### Scenario: Domains migration attempts integration reference - **WHEN** a domains-stream migration is reviewed - **THEN** any reference to an integration schema SHALL be rejected ### Requirement: Baseline migration creates core schema and roles The first core-stream migration SHALL create the `core` schema, the `core` role triple, the core table grants, the `member_console` login role's membership in `core_writer`, all 38 end-state domain tables (plus the `product_kinds` view) in FK-dependency order, and the shared `public` functions. Integration schemas, role triples, grants, and `member_console` memberships SHALL NOT be created by the core stream — each integration's own baseline creates them. The combined baselines SHALL remain structurally complete against the superseded 10-source chain — the same object set (tables, columns, constraints, indexes, triggers, functions, net grant effects, seed rows) modulo the rename map and the documented drop list. #### Scenario: Baseline on fresh install - **WHEN** all baseline migrations run on an empty database - **THEN** the normalized object set SHALL equal that of a database migrated through the superseded per-module chain (reachable via git history), after applying the five-schemas→core rename and the documented drop list #### Scenario: Core baseline creates no integration objects - **WHEN** only the core stream has run - **THEN** no `stripe_*` or `fedwiki_*` role and no `stripe` or `fedwiki` schema SHALL exist ### Requirement: Per-stream version ledgers Each migration stream SHALL track its applied versions in its own goose ledger table, named `goose_db_version_` (`_core`, `_domains`, `_fedwiki`, `_stripe`). Streams SHALL keep their native `00001…` file numbering; no global version namespace SHALL exist, and a stream's version numbers SHALL be independent of its position in the source list. #### Scenario: Fresh install creates one ledger per stream - **WHEN** migrations run against an empty database - **THEN** `goose_db_version_core`, `goose_db_version_domains`, `goose_db_version_fedwiki`, and `goose_db_version_stripe` SHALL exist - **AND** no shared `goose_db_version` table SHALL be created #### Scenario: Adding an integration renumbers nothing - **WHEN** a new integration stream is registered in `migrate.Sources()` - **THEN** existing streams' ledgers and version numbers SHALL be unchanged - **AND** the new stream SHALL start at its own version 1