Files
brainwave/docs/decisions.md
T

173 lines
12 KiB
Markdown
Raw Normal View History

# 架构决策与未决问题
> 状态:持续维护
> 规则:已接受决定不得被实现者静默推翻;变更需新增记录并说明迁移影响
## 决策状态
- `Accepted`:当前实现必须遵守。
- `Proposed`:有推荐默认值,但仍允许产品确认前调整。
- `Superseded`:已被后续决策替代,保留历史原因。
- `Rejected`:明确不采用,避免重复讨论。
## ADR-001:使用原生 Android Kotlin + Jetpack Compose
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:Android 首版采用 Kotlin、Jetpack Compose、Material 3 和单 Activity。
- 原因:产品只要求 Android;Compose 支持自定义主题、Canvas 卦象、状态驱动界面、无障碍和自适应布局,且符合官方新应用方向。
- 后果:不建立 Flutter、React Native 或 WebView 双栈;UI 测试使用 Compose 工具链。
## ADR-002:以官方单模块 Architecture Template 为骨架
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:从 `android/architecture-templates` 的 `base` 分支起步,保留单 Gradle module,按层和 feature 分包。
- 原因:模板包含 Compose、Room、Hilt、ViewModel、Navigation、Flow 和测试基础;MVP 规模不足以抵消多模块复杂度。
- 后果:满足[系统架构](architecture.md#11-多模块化触发条件)后才能提出多模块 ADR。
## ADR-003:起卦是本地确定性纯领域逻辑
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:`CastEngine` 为纯 Kotlin,无随机、时间、网络、存储和问题文本依赖。
- 原因:原始需求明确 AI 不参与起卦;纯函数最容易穷举验证和复现。
- 后果:任何“个性化卦象”“AI 校正”“服务器起卦”均违反架构。
## ADR-004:用户真实投币并手动录入
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:MVP 不提供随机起卦、摇手机起卦或 AI 代投;每轮录入三枚“字/背”。
- 原因:保留用户亲手完成过程的产品核心,并避免将流程游戏化。
- 后果:可以改进录入控件,但不能增加默认随机按钮。
## ADR-005:固定 coin-v1 计值和 bottom-up 顺序
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:字=2、背=3;第一次为初爻;6/9 动、7/8 静;数据标准顺序 bottom-up。
- 原因:传统资料在币面称呼上并不完全一致,项目需要可见且可复现的单一约定。
- 后果:界面始终显示计值;改变约定必须增加方法版本,不能修改旧记录。
## ADR-006:东方文化采用内容优先的纸墨朱砂设计
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:以暖纸、墨色、克制朱砂、宋/黑体搭配和留白建立文化气质,同时保留原生 Android 交互语义。
- 原因:流程和文字比装饰符号更能表达文化,也更利于阅读和无障碍。
- 后果:拒绝龙凤祥云、金色发光、正文毛笔字、旋转太极和抽卡式动效。
## ADR-007:本地内容是核心,AI 是可关闭增强
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:64 卦、卦辞、爻辞和基础解释本地可用;AI 通过独立接口和功能开关接入。
- 原因:核心流程要离线可靠,模型故障和成本不能阻塞产品。
- 后果:P4 本地版可独立发布;AI 关闭时 UI 不能出现断裂占位。
## ADR-008:正式 AI 密钥只放后端
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:正式版经自有后端代理模型服务,不将开发者密钥打入 APK。
- 原因:客户端秘密可以被提取;后端也承担限流、schema 校验、安全策略和版本控制。
- 后果:没有后端之前只实现本地版或 mock,不用临时硬编码密钥“先跑起来”。
## ADR-009:历史记录采用明确保存、本机优先
2026-08-04 23:00:05 +08:00
- 状态:`Superseded by ADR-012`
- 日期:2026-08-04
2026-08-04 23:00:05 +08:00
- 关联:FR-H-001~006、原 TBD-007、2026-08-04 用户确认
- 决定:用户完成后明确点击保存才进入 Room;固定保存可复核的起卦快照,问题原文和当前解释分别使用默认关闭的独立选项;默认无账号和云同步。
- 原因:问题可能高度敏感,自动永久保存不是安全默认值。
2026-08-04 23:00:05 +08:00
- 后果:首页在存在记录时显示“最近一次 / 问卦簿”次级入口,但“开始一问”仍是唯一主操作;没有记录时提供说明和开始入口,不显示空白列表。历史功能不能成为完成起卦的前置条件。
- 取代原因:用户进一步确认“用户写的内容与解读默认完整保存在 App 内”,并要求在首页显式说明、在设置中可关闭;现行规则见 ADR-012。
## ADR-010:多动爻全部透明展示
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:显示所有实际动爻及其文本,不采用未经产品确认的规则隐藏或只选一条“主爻”。
- 原因:不同解释传统存在差异,产品不应把一种裁决算法伪装成唯一事实。
- 后果:AI 可以组织内容,但输入和界面保留全部动爻。
2026-08-04 17:54:15 +08:00
## ADR-011:先确认可点击原型,再固化 Compose 页面
- 状态:`Accepted`
- 日期:2026-08-04
- 关联:[移动端交互原型](prototype.md)、[实施计划 P-1](implementation-plan.md#2-p-1可点击原型与视觉确认)
- 决定:Android 工程初始化前先产出可点击高保真原型和关键状态截图,由用户确认视觉与流程后再实现 Compose 页面。
- 原因:手动录入、结果层级、AI 同意门和东方文化表达都具有较高体验返工成本,先在无构建成本的原型中验证更易调整。
- 备选:Android 骨架初始化后直接实现 Compose;低保真线框图。
- 后果:原型是体验契约与评审证据,但不是生产领域代码;P1 必须独立实现和穷举验证算法,P3 复用已确认的设计令牌与状态关系。
- 复审条件:用户否定当前流程或目标平台发生变化。
2026-08-04 23:00:05 +08:00
## ADR-012:完整记录默认在本机自动保存,可配置关闭
- 状态:`Accepted`
- 日期:2026-08-04
- 关联:FR-H-001~008、取代 ADR-009、解决原 TBD-008、2026-08-04 用户确认
- 决定:
1. 第六爻确认并锁定 `CastResult` 后,只要“自动保存完整记录”开启,就立即在 Room 创建可复核的会话快照;未完成的问题与投币草稿仍只用于会话恢复,不进入长期历史。
2. `autoSaveHistory`、`saveQuestionText`、`saveExplanationContent` 和 `saveActionNote` 默认均为开启。关闭总开关后不再自动写入历史,三个内容开关同步停用;用户仍可在当次结果页选择“保存本次”。
3. 当次随后生成的本地或 AI 解读,在对应设置开启时关联到同一会话;重新解读不得静默覆盖已保存内容,必须新增版本或由用户明确确认替换。
4. 首页固定显示“起卦与历史默认保存在本机,不主动上传。只有你选择 AI 解读时,本次所需内容才会发送。”,并提供设置入口。结果页提供明确的自动保存状态和“本次不保存/删除本次记录”。
5. 问卦簿支持单条删除和清空全部;删除会级联移除会话、解读和行动记录。
6. 历史数据库及其问题、解读和行动数据默认排除 Android Auto Backup、设备到设备迁移的数据提取规则和任何云备份;MVP 不提供账号或云同步。未来备份、导出或同步必须另立决策并取得单独、明确的用户选择。
7. 本地自动保存与 AI 数据发送授权完全独立。自动保存开启、历史中已有内容或用户曾同意旧请求,都不能触发网络调用;每次 AI 调用仍须满足 AI 调用门。
- 原因:完整的起卦、问题、解读与行动记录共同构成可回顾的个人反思日志;默认保存能避免用户遗漏,但必须通过首页透明告知、设置和单次退出能力保留控制权。
- 未采用:继续要求每次手动保存会频繁丢失预期记录;默认只保存卦象快照会让问卦簿缺少上下文,削弱回顾价值。
- 后果:P4 必须实现保存策略、事务关联、设置、单次退出、删除、迁移和备份排除测试;ADR-012 是生产事实源,决策时尚未同步的 v0.2 原型只能作为旧流程评审材料,现行 v0.3 已完成同步。
- 复审触发:引入账号、导出、云同步、系统备份、跨设备迁移或新的隐私/合规要求。
## ADR-013:仓库门禁采用无外部依赖脚本并由 Gradle 聚合
- 状态:`Accepted`
- 日期:2026-08-04
- 关联:解决 TBD-012、P0/P1/P2/P6
- 决定:在 Android application 壳建立前,使用仓库内 Node 脚本执行格式、文档链接、高置信 secret scan、domain 依赖边界、原型语法和内容完整性检查;Gradle 8.2 的 `verifyLocal` 聚合这些检查与 JVM 测试,CI 调用同一入口。
- 原因:当前系统已实测 Node 22、JDK 17 和 Gradle 8.2,且无需新增全局工具或把个人路径写进工程;门禁错误能够给出可修复的文件位置。
- 后果:`spotlessCheck` 当前是仓库内的无依赖格式兼容入口,不表示已引入 Spotless 插件。Android 壳建立后必须把 lint/debug build 加入 `verifyLocal`;将来若采用维护良好的专用插件,应保留命令兼容或同步更新 CI 与文档。
- 复审触发:Android 工具链启用、现有脚本无法表达新边界,或专用工具能以可接受成本提供明显更强的检查。
2026-08-07 18:00:38 +08:00
## ADR-014:正式 Android 身份与最低版本
- 状态:`Accepted`
- 日期:2026-08-07
- 关联:解决 TBD-001 中的产品名、TBD-002、TBD-003;2026-08-07 用户确认
- 决定:正式产品名为“灵机”;用户提供的组织域名 `flash.opcapp.net` 按 Android 反向域名规则映射为 namespace/application ID `net.opcapp.flash`;`minSdk=26`。仓库根名 `brainwave` 继续只作内部代码代号,不进入 Android 发布身份。
- 原因:稳定的正式身份是创建可安装应用、生成类路径、配置清单和建立设备测试的前置条件;API 26 与已接受架构及当前依赖兼容。
- 备选:使用临时 application ID 会形成迁移、数据目录与签名身份风险;继续等待会阻塞 P0。`flash.opcapp.net` 不能直接作为 Android application ID,因为发布标识采用反向域名。
- 后果:主代码迁移到 `net.opcapp.flash`;Android 壳使用“灵机”和 `minSdk=26`。应用图标、商店文案和签名仍未由本决策确认。当前 `compileSdk/targetSdk=34` 是已安装工具链基线,不代表永久商店目标;发布前须按当时商店要求复核升级。
- 复审触发:组织域名所有权变化、发布账号要求更换 ID,或依赖/覆盖率数据要求提高最低系统版本。application ID 一旦发布不得轻率更换。
## 未决问题
| ID | 问题 | 推荐默认 | 阻塞阶段 |
|---|---|---|---|
2026-08-07 18:00:38 +08:00
| TBD-001 | 应用图标与商店素材 | 产品名已由 ADR-014 确认为“灵机”;图标不使用未确认成稿 | P6 商店配置 |
| TBD-004 | 问题是否允许留空 | 允许选择“不写具体内容”,但需显式操作 | P3 |
| TBD-005 | 经典原文、现代白话的版本与授权 | 自有白话 + 可核验公版原文 | P2,发布阻塞 |
| TBD-006 | 乾用九、坤用六是否纳入 MVP | 内容具备时展示 | P2/P3 |
| TBD-009 | AI 模型供应商与自有后端 | 供应商无关接口;先交付本地版 | P5 |
| TBD-010 | 服务端问题/回复保留期 | 最小化且明确披露,优先不持久化正文 | P5,发布阻塞 |
| TBD-011 | 高风险本地资源表覆盖地区 | 首发市场确认后维护,不让模型编号码 | P5 |
## 新增决策模板
```markdown
## ADR-NNN:标题
- 状态:Proposed | Accepted | Superseded | Rejected
- 日期:YYYY-MM-DD
- 关联:需求、issue 或旧 ADR
- 决定:一句可执行结论
- 原因:为什么现在这样选择
- 备选:认真考虑过什么
- 后果:实现、迁移、测试和文档影响
- 复审条件:何时重新评估
```
不要直接改写旧 ADR 来隐藏历史。需要反转时新增 ADR,并把旧记录标为 `Superseded by ADR-NNN`。