Atlas Migration Implementation Plan
給 agentic workers: 執行本計劃時必須使用
superpowers:subagent-driven-development或superpowers:executing-plans,逐項完成 checkbox (- [ ]) 步驟。
目標: 將 services/api 的 schema 管理由 GORM AutoMigrate 遷移到 Atlas,同時不削弱既有資料庫 invariant,也不送出不安全的 baseline。
架構: Atlas 遷移原本規劃拆成多個小型、可獨立 review 的 PR。2026-05-10 後續決策改為由 PR #588 一次交付完整 runtime migration path,因為專案尚未正式上線,保留半套 migration ownership 反而會增加風險。下方 PR 1-4 保留為 historical plan,不再作為新的 implementation checklist。
Tech Stack: Go、GORM、PostgreSQL、Atlas、atlas-provider-gorm、GitHub Actions。
Issue And Artifact References
- GitHub issue:
#463[backend] Migration 工具遷移:GORM AutoMigrate → Atlas - 前置討論:
#214 - Migration 目錄:
services/api/migrations/ - Atlas 設定檔:
services/api/atlas.hcl - API server entrypoint:
services/api/cmd/server/main.go - GORM model structs:
services/api/internal/models/ - Atlas loader model list:
services/api/internal/schema.AtlasSchemaModels()
Current Source Of Truth
目前 develop 的實作狀態:
- Atlas migration directory is active at
services/api/migrations/. - Docker entrypoint applies migrations before starting
air,/tachigo, ortachigo. - Local
make rundepends onmake migrate;make run-no-migrateis the explicit skip path. - API binary must not execute schema DDL; startup guard tests enforce this boundary.
- GORM model structs remain the Atlas loader schema source via
AtlasSchemaModels(), not a runtime migration owner. - Legacy raffle token hash repair remains a data-only startup repair.
本文件記錄 issue #463 的遷移背 景與現況;future implementation 必須以上方 current source of truth 和 repo code 為準,不得把 historical checklist 當成新的待辦。
Guardrails
- 歷史 guardrail:原本不得把 tooling、baseline SQL、
AutoMigrateremoval 合併在同一個 PR;PR#588例外承擔完整 runtime path,避免新專案保留半套 schema ownership。 docs/atlas-schema-reconciliation.mdreview 前,不得產生大型 baseline migration。- 不得因為
atlas migrate lint通過就宣稱 baseline 安全;lint 驗證 migration mechanics,不驗證 production data compatibility。 - 不得移除 GORM model structs;
atlas-provider-gorm仍需要讀取它們。 - 不得把 production deploy automation 或 rollback automation 混進
#463;issue 已明確排除。 - 任何改變 runtime schema 行為的 PR,都必須在 PR body 補 staging 驗證說明。
Historical Plan
以下 PR 1-4 是原始拆分計畫,已被 PR #588 取代,不再作為新的 implementation checklist。保留它們只為了說明 Atlas ownership 遷移時的決策脈絡。
PR 1:Atlas Tooling Only
目的: 加入最小 Atlas toolchain,讓開發者可以從 GORM schema source 產生 diff,CI 可以驗證 loader 與 migration directory。
Files:
- Create:
services/api/atlas.hcl - Create:
services/api/cmd/loader/main.go - Create:
services/api/tools.go - Modify:
services/api/go.mod - Modify:
services/api/go.sum - Modify:
.github/workflows/ci.yml - Optional:
.gitattributes,僅在這顆 PR 開始導入 Atlas checksum 檔時需要
明確不做:
- 不新增
020_atlas_baseline.sql - 不新增
atlas.sum,除非這顆 PR 明確開始讓 Atlas 管理 migration directory checksum - 不移除
AutoMigrate - 不清理 model tags,除非是 loader compilation 的必要前置
Implementation Steps:
- 新增
services/api/tools.go,使用//go:build tools,並以 blank import 錨定ariga.io/atlas-provider-gorm/gormschema或所選 provider 版本需要的 package path。 - 新增
services/api/cmd/loader/main.go,載入目前所有 GORM model structs。 - 將 GORM 無法表達的 custom PostgreSQL schema objects 放進 loader output,或集中在清楚命名的 loader helper。
- 新增
services/api/atlas.hcl,使用external_schemaprogram 執行 loader。 - 新增 CI job,驗證 Atlas 可以 inspect GORM loader,並把 migration directory 套到 clean PostgreSQL。
- 確認 CI migration apply 只寫入 job 內 ephemeral database,不會對共用 database apply migration。
Verification:
cd services/api
go mod tidy
go run ./cmd/loader/main.go > /tmp/tachigo-gorm-schema.sql
atlas schema inspect --env gorm --url env://src --format '{{ sql . }}' > /tmp/tachigo-atlas-inspect-schema.sql
Expected:loader 可編譯、可輸出 PostgreSQL DDL,且 Atlas 可用 env://src 讀取 GORM schema source;這個 smoke test 不應寫入 migration file 或 atlas.sum。
Commit Message:
chore: add atlas migration tooling
refs #463
Co-Authored-By: Codex <codex[bot]@openai.com>
PR 2:Schema Reconciliation
目的: 在撰寫 baseline SQL 前,先把隱性的 schema history 變成可 review 的 reconciliation 文件。
Files:
- Modify:
docs/atlas-schema-reconciliation.md
Implementation Steps:
- 將
services/api/migrations/001-019依序 apply 到乾淨 PostgreSQL dev database。 - 用另一個乾淨 PostgreSQL dev database 啟動目前 server,讓
AutoMigrate與 runtime patches 跑完。 - 用
pg_dump --schema-only --no-owner --no-privilegesdump 兩邊 schema。 - 用 Atlas 或文字 diff 比對兩個 schema state。
- 將每個差異填進 reconciliation table,並寫出明確決策。
- 每個差異都標成
preserve、drop after review、model drift、runtime patch、data migration或out of scope。
Verification:
cd services/api
docker compose run --no-deps --rm app go test ./...
Expected:文件 PR 不改 runtime,後端測試應通過;若本機 Docker 不可用,PR body 必須明確說明未跑原因。
Commit Message:
docs: reconcile atlas migration baseline inputs
refs #463
Co-Authored-By: Codex <codex[bot]@openai.com>
PR 3:Baseline Strategy And Migration Directory Ownership
目的: 建立第一個 Atlas-owned migration state,同時避免破壞既有環境。
Files:
- Create or modify:
services/api/migrations/020_atlas_baseline.sql - Create or modify:
services/api/migrations/atlas.sum - Modify:
docs/atlas-schema-reconciliation.md
Baseline Decision:
採 Apply-safe reconciliation。001-019 繼續作為歷史 migration,020_atlas_reconcile_current_schema.sql 補齊 GORM AutoMigrate/runtime patch 曾經隱式建立的 current schema,並使用 IF NOT EXISTS / guarded DO $$ blocks 避免對既有 DB 重打完整 schema。
Implementation Steps:
- 根據已 review 的 reconciliation 文件產生候選 baseline。
- 驗證 baseline 可套到 empty database。
- 驗證 baseline 可套到已跑過目前 server
AutoMigrate的 database。 - 驗證 baseline 保留 GORM tags 無法表達的 partial indexes 與 constraints。
- 最終 SQL review 完成後,才重新產生
atlas.sum。
Verification:
cd services/api
atlas schema inspect --env gorm --url env://src --format '{{ sql . }}' > /tmp/tachigo-atlas-inspect-schema.sql
atlas migrate apply --dir "file://migrations" --url "$ATLAS_VERIFY_DATABASE_URL"
docker compose run --no-deps --rm app go test ./...
Expected:Atlas 可以 inspect loader、完整 migration directory 可套到乾淨 PostgreSQL、後端測試通過,且 PR body 明確寫出採用哪個 baseline strategy。
Commit Message:
chore: add atlas baseline migration
refs #463
Co-Authored-By: Codex <codex[bot]@openai.com>
PR 4:Remove Runtime AutoMigrate After Reconciliation Migration(範圍完成於 PR #588;deploy follow-up #212)
目的: 讓 Atlas 成為 runtime schema owner。
Files:
- Modify:
services/api/cmd/server/main.go - Modify or add:
services/api/cmd/server相關測試,若現有 coverage 無法驗證 startup migration 行為 - Modify:
docs/atlas-schema-reconciliation.md
Preconditions:
- PR 1、PR 2、PR 3 已 merge。
- Reconciliation migration 已保留 runtime schema patches 與 high-risk historical invariants。
- PR body 必須列出移除哪些 runtime patches,以及等價 Atlas migration 在哪裡。
Implementation Steps:
- 從 server startup 移除
db.AutoMigrate(...)(PR#588)。 - Manual schema patches 已移入 Atlas;legacy raffle token hash repair 保留為非 schema data repair;startup guard test 防止 API process 再執行 schema DDL(PR
#588)。
Verification:
cd services/api
docker compose run --no-deps --rm app go test ./...
Expected:後端測試通過,且 server startup 不再執行 schema DDL。
Commit Message:
chore: remove gorm automigrate from server startup
refs #463
Co-Authored-By: Codex <codex[bot]@openai.com>