# INV-5: Migration Compatibility Matrix

**版本**：v1.0 初稿
**日期**：2026-05-03（台北）
**範圍**：`apps/api/pyproject.toml` 實際版本 + AwoooP Phase 1 migration 需求
**用途**：Phase 1 schema migration 前確認版本相容性，避免 breaking change 踩雷

---

## 1. 當前依賴版本（從 pyproject.toml 實際讀取）

| 套件 | 當前約束 | 對 AwoooP Phase 1 的影響 |
|------|---------|------------------------|
| Python | >=3.11 | ✅ 足夠；`asyncio.TaskGroup`（3.11）可用 |
| FastAPI | >=0.115.0 | ✅ 0.115 已支援 lifespan context manager |
| SQLAlchemy | **>=2.0.0** ✅ 已是 2.x | ✅ `mapped_column`、`DeclarativeBase`、async session 均可用 |
| Pydantic | **>=2.5.0** ✅ 已是 v2 | ✅ `model_validator`、`field_validator` v2 語法可直接用 |
| pydantic-settings | >=2.1.0 | ✅ |
| asyncpg | >=0.29.0 | ✅ 支援 PostgreSQL 15/16 |
| redis | >=5.0.0 | ✅ redis-py 5.x 支援 cluster mode + NX |
| Alembic | **未列出**（透過 SQLAlchemy 間接）| ⚠️ 需確認版本；建議鎖定 >=1.13.0 |
| Langfuse | >=2.0.0,<3.0.0（鎖定）| ✅ 無 AwoooP 影響，保持不變 |
| opentelemetry | >=1.20.0 | ✅ GenAI semantic conventions 需要 >=1.25.0（ADR-121）|
| claude-agent-sdk | >=0.1.50 | ✅ 已使用 |

---

## 2. 重要發現

**✅ SQLAlchemy 已是 2.x**：MASTER-WORKPLAN 中「ORM 1.x vs 2.x」問題**已不存在**。
- `mapped_column`、`relationship`、async session 均可正常用
- 但仍需確認：AwoooP 新 model 使用正確的 2.x 寫法（`DeclarativeBase` 而非舊 `declarative_base()`）

**✅ Pydantic 已是 v2**：六合約 Pydantic models 可直接用 v2 語法。

**⚠️ Alembic 版本未鎖定**：
- `apps/api/migrations/` 有大量 `.sql` 手寫 migration（非 Alembic auto-generate）
- AwoooP Phase 1 要決定：繼續手寫 SQL migration，還是引入 Alembic autogenerate？
- **建議**：保持手寫 SQL（現有慣例），每個 migration 強制有 down migration

**⚠️ OTel 版本需升級（ADR-121）**：
- GenAI Semantic Conventions 需要 `opentelemetry-semantic-conventions>=0.46b0`（含 `gen_ai.*` namespace）
- 當前 `>=1.20.0` 可能不夠，需要 `>=1.25.0`
- 升級風險：低（向後相容）；需要加入 `opentelemetry-semantic-conventions` 依賴

---

## 3. AwoooP Phase 1 新依賴需求

| 套件 | 版本 | 用途 | 是否需要新增 |
|------|------|------|------------|
| `pg_partman`（PostgreSQL extension）| >=4.7.0 | 高流量表 partition 自動管理 | ⚠️ 需在 DB 層安裝，非 Python 套件 |
| `opentelemetry-semantic-conventions` | >=0.46b0 | OTel GenAI attribute（ADR-121）| ✅ 需新增到 pyproject.toml |
| `PgBouncer` | >=1.21.0 | Connection pool（tool-expert 需求）| ⚠️ Infrastructure，非 Python 套件 |

---

## 4. Migration SQL 版本相容性

| Migration 問題 | 當前狀態 | 修補方式 |
|---------------|---------|---------|
| `adr105_mcp_audit_snapshots.sql` 建表 `mcp_audit_snapshots`，但 models.py 用 `mcp_audit_log` | ⚠️ 衝突 | 確認是否已 rollback；若 `mcp_audit_log` 是現行表，則 `mcp_audit_snapshots` 已廢棄 |
| 部分 migration 無 rollback SQL | ⚠️ MASTER-WORKPLAN P0-02 | Phase 1 新 migration 強制加 down migration |
| `ARRAY` 型別在部分 migration 用 `TEXT[]`，在其他用 `JSONB` | 🟠 不一致 | AwoooP 新表一律用 `JSONB`（`fix_playbooks_array_to_jsonb.sql` 已示範）|

---

## 5. SQLAlchemy 2.x 正確寫法確認清單（Phase 1 新 model 必須遵守）

```python
# ✅ 正確（SQLAlchemy 2.x）
from sqlalchemy.orm import DeclarativeBase, mapped_column, Mapped
from sqlalchemy import String, DateTime, func
from typing import Optional

class Base(DeclarativeBase):
    pass

class AwooopProject(Base):
    __tablename__ = "awooop_projects"
    project_id: Mapped[str] = mapped_column(String(64), primary_key=True)
    display_name: Mapped[str] = mapped_column(String(128), nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    migration_mode: Mapped[str] = mapped_column(
        String(32), nullable=False, default="legacy_awoooi_default"
    )

# ❌ 舊寫法（SQLAlchemy 1.x，不要用）
# from sqlalchemy.ext.declarative import declarative_base
# Base = declarative_base()
# project_id = Column(String(64), primary_key=True)
```

---

## 6. 版本兼容風險清單

| 風險 | 嚴重度 | 緩解方式 |
|------|--------|---------|
| pg_partman 未安裝，partition 手動建立出錯 | 中 | Phase 1 先不建 partition，只規劃；Phase 4 前安裝 pg_partman |
| OTel semantic conventions 版本不足 | 低 | Phase 4 前升級（非 Phase 1 阻擋項）|
| PgBouncer 未安裝，AwoooP worker 壓力測試可能爆連接 | 中 | Phase 4（platform shell）前安裝 |
| Alembic 未鎖定，CI 環境版本飄移 | 低 | 鎖定 `alembic>=1.13.0` 加入 pyproject.toml |

---

## 7. 驗收標準

- [ ] Phase 1 所有新 migration 使用 SQLAlchemy 2.x `mapped_column` 語法
- [ ] 所有新 migration 有 down migration SQL
- [ ] `mcp_audit_snapshots` vs `mcp_audit_log` 衝突已解決（確認哪個是現行表）
- [ ] `pg_partman` 安裝計畫已寫入 runbook

*最後更新：2026-05-03（台北）*
