架構決策 (ADR)
ADR — Architecture Decision Records
決策記錄庫。每一個影響系統設計的跨 PR、跨 spec 的重要決策都應該在這裡有一個家。
命名規則
NNNN-<kebab-name>.md例:0001-admin-vs-parent-generation-endpoint.md
NNNN:4 位流水序號,全域單調遞增,不重用- 編號與 openspec change(
docs/openspec/changes/NNNN-*/)各自獨立(兩套各自從 0001 起算) - 已 supersede 的 ADR 不刪除,status 改
superseded-by-NNNN - 取號前必查 open PR:下一個號碼可能已被未 merge 的 PR 佔用(不在 main 的 index 內)。
跑
gh pr list --search "ADR in:title"確認後再取號。範例:0013 被 open PR #560 佔用,故 ADR-0014 跳過 0013。 - 並行 ADR PR 預期 index 衝突:兩個 ADR PR 同時開時,下方索引表會在第二個 PR merge 時衝突。
解法是保留兩列、按
NNNN數字序排,不是二擇一。
四條進入路徑
| 路徑 | 工具 | 說明 |
|---|---|---|
| Spectra change archive | /adr archive --change <name> | 從 change 的 design.md D-XX 拆出 |
| PR retro 升格 | /adr from-retro | 從 /pr-retro 的 lesson 升格 |
| Design 取捨偵測 | /design-ready 偵測 | Figma 設計決策(token/layout 取捨) |
| 手動建立 | /adr new "<title>" | 非 spectra 開發的決策(bug fix、refactor) |
索引
| ID | 決策 | Status | 關聯 |
|---|---|---|---|
| 0001 | Admin 生成 endpoint 獨立於家長端 | accepted | change 0013, PR 待補 |
| 0002 | GenerationJob nullable family/child binding | accepted | change 0013 |
| 0003 | IoT Simulator 在 FastAPI server-side 運行 | accepted | archive/2026-04-09 |
| 0004 | IoT Simulator WebSocket per device session | accepted | archive/2026-04-09 |
| 0005 | 人機協作文件六層架構與 Sync Pipeline | proposed | change 0015 |
| 0006 | Spectra 五尺度約束升級與 Upstream Drift 防護 | proposed | issue #384 |
| 0007 | Sub-agent Context Boundary — 角色分工與 handoff 協議 | accepted | — |
| 0008 | BDD Spec-Test Traceability — docstring trace 機制 | accepted | change 0018;issue #720(Q2 裁決,漸進式 ratchet) |
| 0009 | Harness Engineering 採納策略 — Fowler 框架對照 YiBi MVP | proposed | — |
| 0010 | 棄用 MQTT 改採 HTTPS Event-Driven + WSS + Offline-First | accepted | change 0022 |
| 0011 | 多孩子共用裝置 — persona binding 持有裝置端身份;is_current 留在 App 端 | accepted | change 0023-e01-multi-child-persona-binding, change 0024-e02-current-child-and-soft-delete-cascade |
| 0012 | 採用 Clean Architecture repository pattern + 完整 domain entity 分離(漸進遷移) | accepted | change 0027, PR 487 |
| 0013 | Celery worker/beat 遷移至 Cloud Run worker_pool + RedBeat scheduler | proposed | issue #308, #309, #310 |
| 0014 | 媒體儲存雙軌 — 精選用 Cloudflare R2 + CDN;UGC 用 GCS Signed URL | proposed | spec E15-ugc-playback, archive/2026-05-04-media-delivery-cdn-strategy, issue #368/#369 |
| 0015 | 臨時內容即時下發機制(on-demand content push)— offline-first 下的低延遲下發選項 | accepted | 採 C(change 0034);B 後續待晶片廠商討論;延伸自 ADR-0010 |
| 0016 | Firebase Auth 當 identity broker + 後端 token exchange,保留自有 JWT 與 device HMAC | proposed | spec E01-auth-family |
| 0017 | Firebase 治理 — Terraform 管結構、CLI/CI 管部署、config 改 secret 注入 | accepted | PR 574;deciders howie & keith;相關 ADR-0016、ADR-0020 |
| 0018 | 固定 Flutter SDK 版本於 CI 與本機 toolchain(停用 stable channel 浮動) | accepted | ADR PR 578; 實作 PR 546/547 |
| 0019 | TestFlight 內測 build numbering(run_number + OFFSET)與 mobile/backend 版本脫鉤 | accepted | change 0035(PR1)、0036(PR2);deciders howie & keith |
| 0020 | Google Play AAB 上傳採 keyless WIF(r0adkll/upload-google-play + google-github-actions/auth),不建 SA JSON key | accepted | PR 591;deciders howie & keith;相關 ADR-0017 |
| 0021 | Spec 一致性 CI 把關用開源 @fission-ai/openspec CLI(ubuntu),Spectra 專屬 drift/analyze/verify 留 Mac 手動 scan | accepted | PR 660;N/A(純 CI/toolchain);相關 ADR-0006;issue #720(Q1 裁決,Phase 2 dry-run) |
讀 ADR 的時機
- 某個設計看起來很奇怪 → 先查這裡,可能有歷史原因
- 新人 onboarding → 把 ADR 0001-000N 當作系統設計入門讀物
- 要做跟歷史決策相反的改動 → 先查對應 ADR,確認假設是否仍成立
寫 ADR 的時機
- 有兩個以上合理的方案,但你選了其中一個
- 這個決策日後可能被推翻或重新評估
- 有非顯而易見的限制條件(安全、合規、效能、外部依賴)
- bug fix 中發現了一個系統性 pattern 值得記錄
ADR 撰寫注意事項
Frontmatter 必須在同一 PR 填完所有欄位再送 review:
| 欄位 | 填法 | 說明 |
|---|---|---|
related.change | openspec change 名稱(如 0017-xxx);無對應 change 時可留空或標 N/A + 理由註解(見下方 N/A convention) | 建立 L3 ADR ↔ L2 change 雙向連結 |
related.pr | 本 PR 號碼(如 386),強制填入引入此 ADR 的 PR | 記錄引入此決策的 PR |
related.jira | Jira key(如 YB-123)或空字串 | 有 ticket 時填入 |
related.pr 強制填入引入此 ADR 的 PR 號——空 related.pr 會斷掉追蹤鏈,
AI reviewer 會標 Critical,PR review 時無法通過。
related.change 的 N/A convention
related.change 是否必填視 ADR 類型而定:
-
有對應 OpenSpec change(多數功能 / 契約 / 資料模型決策)→ 必填 change 名稱, 建立 L3 ADR ↔ L2 change 雙向連結;此情形下留空會斷鏈,AI reviewer 標 Critical。
-
無對應 change(純 CI / toolchain / process 類決策,如 ADR-0007 / 0009 / 0013 / 0018)→ 可將
related.change留空或標N/A,並在同一行加理由註解,例如:change: "" # N/A:純 CI/toolchain 決策,無對應 OpenSpec change此情形不算斷鏈、不標 Critical——因為本來就沒有對應的 L2 change 可連結。 注意:此放寬僅限
related.change;related.pr在任何情形皆強制,不放寬。