docs: add Android app implementation harness

This commit is contained in:
QiuSW
2026-08-04 17:09:52 +08:00
commit fdb20b8ae5
13 changed files with 1799 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
# Brainwave 项目文档
> 文档状态:方案基线
> 最后核验:2026-08-04
> 当前阶段:Android 工程尚未初始化,需求与工程约束已建立
本目录是 Brainwave 的项目知识事实源。产品决策、领域算法、架构边界、验收标准和已知失败模式必须写入仓库;聊天记录、口头约定和临时提示不构成项目规范。
## 项目一句话
Brainwave 是一个以《易经》三枚铜币法为文化背景的 Android 个人反思工具:用户亲手投掷并录入六次结果,应用在本地确定本卦、之卦和动爻;用户主动点击「解」后,本地内容或 AI 才把既有结果翻译成现代语言,并收束到一项低风险、可撤销的现实行动。
## 文档地图
| 文档 | 何时阅读 | 权威内容 |
|---|---|---|
| [原始需求](原始需求.txt) | 核对产品最初意图时 | 用户提供的原始需求,不在此文件中扩写 |
| [产品规格](product-spec.md) | 判断功能范围和验收结果时 | 目标、非目标、需求编号、MVP 边界 |
| [领域规则](domain-rules.md) | 修改投币、六爻、卦象映射时 | 唯一允许的起卦算法与不变量 |
| [UX 与东方视觉](ux-design.md) | 修改页面、文案、动画和主题时 | 用户流程、文化表达、无障碍标准 |
| [系统架构](architecture.md) | 新增包、依赖、数据源或网络能力时 | 分层、依赖方向、运行时数据流 |
| [数据与内容](data-content.md) | 修改卦库、历史记录或内容来源时 | 数据契约、授权、隐私和迁移规则 |
| [AI 解释与安全](ai-safety.md) | 修改提示词、模型调用或解释结果时 | AI 调用门、输入输出契约和安全边界 |
| [质量门禁](quality-gates.md) | 实现、评审、发布前 | 自动化验证、需求追踪和完成定义 |
| [实施计划](implementation-plan.md) | 领取任务或判断下一步时 | 阶段、依赖、交付物和退出条件 |
| [决策记录](decisions.md) | 遇到架构分歧或未决问题时 | 已接受决定、默认假设和 TBD |
| [代理工作手册](agent-playbook.md) | 任何编码代理开始工作前 | 检索、修改、验证和交付流程 |
| [失败记忆](failure-memory.md) | 排障、复盘或添加防回归规则时 | 已知风险、症状、护栏和验证方式 |
## 推荐阅读路径
- 实现起卦:本页 → [领域规则](domain-rules.md) → [系统架构](architecture.md) → [质量门禁](quality-gates.md)
- 实现界面:本页 → [产品规格](product-spec.md) → [UX 与东方视觉](ux-design.md) → [系统架构](architecture.md)
- 接入 AI:本页 → [AI 解释与安全](ai-safety.md) → [数据与内容](data-content.md) → [质量门禁](quality-gates.md)
- 修复缺陷:本页 → [失败记忆](failure-memory.md) → 对应领域文档 → [质量门禁](quality-gates.md)
- 引入依赖或调整结构:本页 → [系统架构](architecture.md) → [决策记录](decisions.md)
## 规范优先级
发生冲突时按以下顺序处理:
1. 用户最新明确决定。
2. [产品规格](product-spec.md)中的验收标准与非目标。
3. [领域规则](domain-rules.md)和[AI 解释与安全](ai-safety.md)中的不变量。
4. [系统架构](architecture.md)中的依赖约束。
5. [UX 与东方视觉](ux-design.md)及其他实施建议。
不能自行消解的冲突必须记入[决策记录](decisions.md),并在继续实现前请求产品决定。
## Harness Engineering 原则
本项目采用以下仓库约束:
- **仓库是事实源**:影响实现的信息必须进入版本化文档、代码、测试或脚本。
- **入口是地图**:本页只负责导航,细节放在专题文档,避免单个巨型说明吞噬上下文。
- **边界优先**:用明确的领域类型、接口、依赖方向和测试表达不变量。
- **验证闭环**:每个需求必须能映射到自动化测试或明确的人工验收步骤。
- **失败可积累**:重复错误进入[失败记忆](failure-memory.md),随后转化为测试、静态检查或更清楚的契约。
- **文档随代码演进**:改变行为的提交必须同步更新对应文档;不能让实现与事实源分叉。
这些原则来自 Harness Engineering 的核心实践:让代理可读取仓库知识、机械执行架构约束,并通过反馈循环验证工作,而不是依赖一次性提示。参考:[OpenAI Harness Engineering](https://openai.com/index/harness-engineering/)。
## 当前已确认与未确认
已确认:
- 目标平台为原生 Android。
- 使用 Kotlin、Jetpack Compose 和单 Activity 架构。
- 起卦完全在本地完成,AI 不得参与或更改起卦结果。
- 用户真实投币并录入;MVP 不提供随机起卦按钮。
- 东方文化表达采用“纸、墨、朱砂、留白”的内容优先风格。
- AI 仅在用户主动点击「解」之后调用。
仍需产品确认的事项记录在[决策记录](decisions.md#未决问题)。任何代理不得把 `TBD` 悄悄变成产品事实。
## 文档维护规则
每份专题文档必须包含状态或适用范围。发生下列变化时必须更新文档:
- 用户可见行为改变;
- 领域算法、数据结构或内容版本改变;
- 新增网络、存储或第三方依赖;
- 测试命令、构建方式或发布门禁改变;
- 出现可能再次发生的缺陷。
文档链接、需求编号和验证命令将随工程骨架一起接入 CI 检查。当前尚无 Gradle 工程,不能声称任何构建或测试已经通过。
+153
View File
@@ -0,0 +1,153 @@
# 编码代理工作手册
> 状态:Harness 基线
> 读者:在本仓库中执行分析、实现、评审和修复的编码代理
## 1. 开始任务
1. 阅读 [文档地图](README.md),再按任务类型读取专题文档。
2. 检查工作树、已有实现和测试;不要假设方案文档已经被编码。
3. 找到关联需求编号、架构边界和完成标准。
4. 把目标拆成可独立验证的小改动,明确不在范围的内容。
5. 只有必要产品选择无法从仓库确定且错误假设风险较高时,才请求用户决定。
根 `AGENTS.md` 创建后应保持为短入口,只包含仓库导航、常用命令和不可违反的不变量,并指向本手册;不要复制全部专题文档。
## 2. 事实源与检索顺序
1. 当前用户任务和已接受 ADR。
2. 对应产品需求、领域规则、AI 安全和架构文档。
3. 自动化测试与 schema。
4. 代码实现。
5. 外部官方文档。
测试、代码和文档互相矛盾时,不要随意选择最方便的一方。先判断哪一个表达了当前已接受行为;修复时同步其余事实源,并在交付说明中指出冲突。
代码发现优先使用仓库配置的知识图谱工具:`search_graph` → `trace_path` → `get_code_snippet` → `query_graph` → `get_architecture`。图工具不足,或搜索字符串、配置和非代码文件时,再使用 `rg`。
## 3. 永久不变量
任何任务都不得违反:
- AI、网络、时间、问题文本和随机数不参与起卦。
- 六爻领域顺序为 bottom-up;6/9 动,7/8 静。
- 第六轮确认后,AI 和解释层不能修改 `CastResult`。
- 核心起卦和本地内容离线可用。
- 开发者密钥不进入 APK、源码、日志和测试夹具。
- 问题原文和 AI 全文不进入遥测或崩溃日志。
- 未授权或来源不明的现代译文、字体和图像不能进入发布包。
- AI 不预测和替用户作高影响决定。
- 东方文化表达不得牺牲触控、对比度、读屏和字体缩放。
若任务显式要求违反其中一项,停止实现并请求产品确认;确认后先更新决策和规范,再改代码。
## 4. 实现工作流
### 4.1 Inspect
- 确认当前分支/工作树和用户已有改动。
- 阅读最小充分上下文,不进行无目的全仓库扫描。
- 查找已有相似模式、测试 fake、主题令牌和错误类型。
- 确认依赖版本与官方 API,而不是凭记忆猜测会变化的接口。
### 4.2 Plan
- 用需求编号描述结果,而不是只列文件。
- 标记数据迁移、隐私、安全、内容授权和 UI 无障碍风险。
- 优先扩展现有抽象;新抽象必须有两个以上清楚的使用点或隔离重要边界。
### 4.3 Implement
- 做满足验收的最小完整改动。
- 领域逻辑用封闭类型和纯函数,边界数据先解析再进入领域。
- UI 使用不可变状态和显式 action;不在 Composable 中访问数据源。
- 新颜色、字号、间距和文案进入设计/资源令牌。
- 不顺手重构与任务无关的用户代码,不覆盖脏工作树。
### 4.4 Verify
- 先运行最接近改动的测试,再运行 `verifyLocal` 或等价全量门禁。
- UI 变更至少检查正常、加载、错误、空、离线和恢复状态。
- 领域变更运行穷举/属性测试。
- AI 变更使用 fake server,不依赖实时模型作为唯一证据。
- 明确记录执行过、未执行和失败的命令;不把预期当结果。
### 4.5 Document
以下变化必须同步文档:
- 用户可见行为 → [产品规格](product-spec.md)/[UX](ux-design.md)
- 算法或模型 → [领域规则](domain-rules.md)
- 包、依赖或数据流 → [系统架构](architecture.md)
- schema、来源、隐私 → [数据与内容](data-content.md)
- 提示词/模型行为 → [AI 安全](ai-safety.md)
- 命令/门禁 → [质量门禁](quality-gates.md)
- 架构取舍 → [决策记录](decisions.md)
- 可复现失败 → [失败记忆](failure-memory.md)
## 5. 依赖政策
引入依赖前回答:
1. 现有 AndroidX/Kotlin/项目代码能否清晰完成?
2. 依赖是否维护、兼容当前工具链并有明确许可证?
3. 它增加多少 APK/构建/运行复杂度?
4. 它会不会模糊领域边界或把密钥带入客户端?
5. 测试如何替换或 fake 它?
新增大型依赖、具体 AI SDK、分析 SDK、字体包或多模块结构必须记录 ADR。
## 6. 测试纪律
- 修复缺陷先建立可失败的回归测试,再修复。
- 不删除、跳过或放宽断言来让门禁变绿,除非需求正式改变。
- 不在测试中使用 `Thread.sleep` 等不稳定等待;使用测试调度器和可控 fake。
- 不把实时日期、网络和模型输出放入确定性测试。
- screenshot golden 的更新必须人工确认差异来源。
- 数据库迁移必须从上一发布 schema 测到当前 schema。
## 7. 失败处理
命令失败时:
1. 保存准确命令、错误摘要和环境。
2. 判断是实现缺陷、环境缺失、测试脆弱还是规范冲突。
3. 修复根因并重新运行最小失败用例。
4. 再运行相关全量门禁。
5. 若相同类别可能复发,更新[失败记忆](failure-memory.md)并增加机械护栏。
不得无限重复同一失败命令,也不得用捕获异常、硬编码测试值或关闭检查掩盖失败。
## 8. 交付格式
最终交付至少说明:
```markdown
## 结果
- 完成了什么,对应哪些需求
## 关键实现
- 重要边界或决策
## 验证
- 实际运行的命令和结果
- 人工验证
## 剩余事项
- 未运行、TBD、外部阻塞或剩余风险
```
如果任务只完成文档或调查,直接说明没有构建/测试,不伪造运行证据。
## 9. 定期维护
每个里程碑执行一次轻量“熵清理”:
- 删除无引用文档、死代码和过期 TODO;
- 合并重复 helper 和重复主题令牌;
- 检查实现是否绕过层边界;
- 检查文档链接、状态和验证命令;
- 检查依赖、许可证、内容版本和数据库迁移;
- 将重复评审意见提升为自动化规则。
目标不是追求文件数量,而是让下一次代理运行能更快找到真实约束,并从环境得到准确反馈。
+150
View File
@@ -0,0 +1,150 @@
# AI 解释契约与安全边界
> 状态:提供方无关的 MVP 契约;模型供应商与后端仍为 TBD
> 核心原则:AI 解释既有结果,不参与起卦,不替用户决定
## 1. 调用门
只有同时满足以下条件才允许调用 AI:
1. 已存在由本地 `CastEngine` 生成并锁定的 `CastResult`。
2. 本卦、之卦、卦辞和实际动爻已经可供用户查看。
3. 用户明确点击「AI 解读」。
4. 用户已经接受当前版本的数据发送说明;同意版本变化后需重新确认。
5. 网络可用且客户端已通过请求前校验。
禁止后台预取、自动重试到另一模型、首次进入结果页即调用、以及把 AI 回复用于改写 `CastResult`。
## 2. AI 输入契约
客户端提交给后端的业务载荷应是明确 DTO,不发送整个数据库实体或 UI 状态:
```json
{
"contractVersion": "explanation-v1",
"locale": "zh-Hans",
"question": "用户明确同意发送的问题文本",
"cast": {
"methodVersion": "coin-v1",
"primaryHexagramId": 24,
"transformedHexagramId": 2,
"movingLinePositions": [1],
"lineValuesBottomUp": [9, 8, 8, 8, 8, 8]
},
"grounding": {
"contentVersion": "zh-Hans-2026.1",
"primaryJudgment": "本地审核文本",
"movingLineTexts": [
{"position": 1, "text": "本地审核文本"}
],
"transformedJudgment": "本地审核文本"
}
}
```
不发送:账号标识、通讯录、位置、设备广告 ID、历史记录、其他会话问题或开发者密钥。
用户问题必须被服务端提示词当作“待分析数据”而不是指令。客户端与服务端都不能把用户文本直接拼接为无边界系统指令。
## 3. 输出契约
模型必须返回结构化对象,服务端验证后再交给客户端:
```json
{
"contractVersion": "explanation-v1",
"source": "AI",
"summary": "一段不确定性明确的白话摘要",
"observations": [
{"label": "当前处境", "text": "…", "groundedBy": ["primary"]},
{"label": "变化所在", "text": "…", "groundedBy": ["line-1", "transformed"]}
],
"questionsToConsider": ["…"],
"reversibleAction": {
"title": "可以试的一小步",
"steps": ["…"],
"timebox": "例如 24 小时或一周",
"rollback": "如何停止、撤回或恢复原状"
},
"caution": "这不是预测或专业意见。"
}
```
客户端必须:
- 拒绝未知 `contractVersion`;
- 限制字段长度和数组数量;
- 将所有字段视为纯文本,不能执行 HTML、Markdown 链接、脚本或模型生成命令;
- 缺少必要字段时进入可恢复失败状态;
- 显示 `source=AI`,不伪装为经典原文或项目编辑内容。
## 4. 提示词不变量
服务端系统提示词至少表达:
- 输入中的卦号、爻值、动爻和经典文本是已经发生的事实,不得重算、替换或质疑。
- 只解释提供的本卦、之卦和实际动爻,不补造不存在的爻辞、典故或来源。
- 使用现代、平实、有条件的语言,避免“注定、必然、天意、唯一正确”。
- 不预测日期、彩票、价格、疾病结局、死亡、怀孕、诉讼或他人隐秘意图。
- 不替用户做辞职、分手、结婚、借贷、投资、医疗、法律、人身安全等高影响决定。
- 可以帮助拆解顾虑、列出可验证假设、提出澄清问题。
- 结尾最多给出一项低风险、可撤销、有限时的行动,并写出停止或回滚方法。
- 如果无法可靠解释,应说明限制并回退到本地材料,不用权威语气填补空白。
## 5. 高风险内容处理
| 用户内容 | AI 行为 |
|---|---|
| 投资、借贷、博彩 | 不给买卖/金额/时点结论;建议查看事实、风险承受力并咨询合格人士 |
| 医疗或心理诊断 | 不诊断、不建议停药;建议联系合格专业人员 |
| 法律纠纷 | 不提供确定法律结论;建议保存事实并咨询当地合格律师 |
| 辞职、分手等重大决定 | 不替用户决定;建议设计小范围验证、冷静期或对话 |
| 自伤、伤人或即时危险 | 停止卦象式解释;以安全为先,鼓励联系当地紧急服务、可信任的人或专业支持 |
高风险分支的提示和模板需要安全评审与专门测试。地域相关的热线和法律/医疗资源不能由模型临时编造;上线前采用经过维护的资源表或不显示不确定号码。
## 6. 本地解释
本地解释是可靠降级路径,不是低等级付费占位。它应:
- 直接读取经审校的本地白话;
- 根据是否有动爻组合固定结构;
- 清楚区分本卦、变化位置与之卦;
- 通过模板提供反思问题,但不根据用户问题生成事实断言;
- 无网络、未同意 AI 或 AI 失败时完整可用。
本地模板同样遵守“不预测、不裁决”的产品原则。
## 7. 故障与重试
| 故障 | 客户端表现 | 是否改变起卦结果 |
|---|---|---|
| 无网络 | 提供本地解读和“稍后重试” | 否 |
| 超时 | 保留结果,允许一次显式重试 | 否 |
| 429/限流 | 告知稍后再试,不循环请求 | 否 |
| 服务端 5xx | 显示通用错误码,保留本地内容 | 否 |
| 非法 JSON/缺字段 | 拒绝渲染,记录脱敏错误码 | 否 |
| 安全拦截 | 使用安全说明或本地材料 | 否 |
重试必须由用户发起或使用受限、可取消的网络策略;不得因重试得到不同回复而覆盖用户已经保存的解释,除非用户明确选择“重新解读”。
## 8. 密钥、日志和服务端
- 正式模型密钥只存在于后端密钥管理系统。
- Android APK 中不包含生产密钥,即使使用混淆、BuildConfig 或 NDK 也不视为安全。
- 发布构建关闭 HTTP body 日志。
- 业务日志使用 request ID、合同版本、状态码、延迟桶和模型配置版本;不记录原始问题与回复。
- 模型、提示词和安全策略都有独立版本,便于复现与回滚。
- 服务端对输入输出做长度、schema、频率和内容安全检查。
Android 端密钥管理参考:[Android 安全清单](https://developer.android.com/privacy-and-security/security-tips)。
## 9. AI 验收测试
- 同一 `CastResult` 在 AI 请求前后字节级确定字段不变。
- 没有 `CastResult`、未点击、未同意或结果尚未显示时,网络 mock 收到 0 次调用。
- 提示注入样例不能让模型重算卦、泄露系统提示或执行用户文本中的指令。
- 高风险测试集不产生确定的医疗、法律、投资或人生决定。
- 所有合法输出都有可撤销行动;所有非法输出都被客户端拒绝且可改用本地解释。
- 网络断开、超时、取消、旋转和进程重建不会重复提交。
- 日志捕获测试中不出现问题原文、回复全文和密钥样例。
+186
View File
@@ -0,0 +1,186 @@
# Android 系统架构
> 状态:已接受的 MVP 架构
> 技术方向:Kotlin + Jetpack Compose,单模块起步
> 骨架来源:[Android Architecture Starter Template — base](https://github.com/android/architecture-templates/tree/base)
## 1. 架构目标
- 本地起卦是确定、可测试、无 Android/网络依赖的纯领域逻辑。
- UI、内容、存储和 AI 可以独立替换,不改变已生成的 `CastResult`。
- 核心流程离线可用,AI 故障只降级解释能力。
- 包边界对人和编码代理都清楚,并能通过测试或静态规则检查。
- MVP 保持单 Gradle 模块,避免过早多模块化;代码仍按领域和层分包。
Android 官方建议新应用采用清晰的 UI/数据分层、单向数据流、ViewModel、协程与 Flow;大型复杂业务才按需要增加 domain 层。本项目因为起卦不变量重要,保留轻量 domain 层。[Android 架构指南](https://developer.android.com/topic/architecture)
## 2. 系统上下文
```text
┌──────────────── Android App ────────────────┐
│ Compose UI │
│ ↓ intent ↑ immutable state │
│ ViewModel / state holder │
│ ↓ │
│ Domain: CastEngine / explanation use cases │
│ ↓ interfaces │
│ Repositories │
│ ├─ Assets / prepackaged content │
│ ├─ Room history │
│ ├─ DataStore settings │
│ └─ AI gateway ───────────────→ App backend│
└─────────────────────────────────────────────┘
```
只有 AI 解释需要网络。起念、投币、起卦、卦库读取和本地解释都位于 Android 应用内部。
## 3. 建议目录
工程初始化后建议采用:
```text
app/src/main/java/<package>/
├── app/
│ ├── BrainwaveApplication.kt
│ ├── MainActivity.kt
│ └── BrainwaveNavHost.kt
├── core/
│ ├── model/ # 跨层不可变领域模型
│ ├── designsystem/ # 主题、字体、间距、卦象 Canvas
│ └── common/ # 极少量通用结果类型/调度器
├── domain/
│ ├── casting/ # CastEngine、映射与校验
│ └── explanation/ # 解释用例与端口
├── data/
│ ├── content/ # assets 卦库解析与版本校验
│ ├── history/ # Room entity、DAO、repository
│ ├── settings/ # DataStore
│ └── ai/ # 网络 DTO、gateway、响应校验
└── feature/
├── onboarding/
├── question/
├── casting/
├── result/
├── explanation/
├── history/
└── settings/
```
测试按相同包结构镜像放入 `src/test` 和 `src/androidTest`。
## 4. 依赖方向
允许:
```text
feature UI → feature ViewModel → domain use case → repository interface
data implementation → repository interface + core model
app/navigation → feature public route
core/designsystem → Compose/Material + core model(仅绘制需要)
```
禁止:
- `domain` 依赖 Compose、Activity、ViewModel、Room、Retrofit/Ktor 或具体 AI SDK。
- `CastEngine` 读取系统时间、随机数、网络、数据库或问题文本。
- Composable 直接访问 DAO、assets、网络客户端或 Hilt entry point。
- `data/ai` 引用或调用 `CastEngine`。
- AI DTO 直接成为 UI 状态;必须先校验并映射为领域结果。
- feature 之间直接引用对方的内部 ViewModel 或 screen 实现。
- 一个“Utils”包承载无边界的杂项业务逻辑。
工程具备代码后,应通过架构测试或静态检查机械执行这些禁止项,不能只依赖评审记忆。
## 5. 核心组件职责
### `CastEngine`
纯 Kotlin、无副作用。输入六轮铜币和方法版本,输出不可变 `CastResult`。所有规则来自[领域规则](domain-rules.md)。
### `HexagramContentRepository`
按卦号和 `contentVersion` 返回已校验的本地内容。缺失、重复或版本不兼容属于数据完整性错误,不使用 AI 猜补。
### `CastingSessionViewModel`
维护草稿问题、六轮录入和状态机。通过 `SavedStateHandle` 保存可恢复的进行中状态;只在第六轮确认时调用 `CastEngine`。
### `HistoryRepository`
在用户明确保存后持久化会话。Room 模型不得泄露到 UI;数据库迁移必须有测试。
### `ExplanationRepository`
提供统一接口:本地解释和 AI 解释返回相同的可展示领域结构,并带明确 `source`。AI 实现遵循[AI 解释与安全](ai-safety.md)。
## 6. 单向数据流
每个 feature 使用不可变 `UiState` 和显式 `UiAction`:
```text
user action → ViewModel → use case/repository → state update → Compose render
```
一次性结果也应建模为状态及消费动作,避免裸 `Channel`/事件在配置变化时丢失。导航由 UI 根据已处理状态触发,并保证重复收集不会重复提交 AI 请求。
领域计算成功后 `CastResult` 为只读值。解释状态与起卦状态分开存储:
```text
ResultUiState(
castResult = immutable,
content = immutable,
explanationState = Idle | Loading | Local | Ai | Failed
)
```
## 7. 数据和存储选择
- **assets 或预置 Room**:版本化的 64 卦及爻辞。首版如果仅按编号读取,JSON assets 更简单;需要全文检索或内容迁移时再采用预置 Room。
- **Room**:用户主动保存的会话、解释和行动记录。Android 官方推荐 Room 管理非平凡结构化数据。[Room 文档](https://developer.android.com/training/data-storage/room)
- **DataStore**:方法说明是否已读、主题、AI 同意版本等少量设置;不保存大型历史或完整卦库。[DataStore 文档](https://developer.android.com/topic/libraries/architecture/datastore)
- **SavedStateHandle**:恢复当前未完成流程;不是长期历史数据库。
详细契约见[数据与内容](data-content.md)。
## 8. 网络与 AI 边界
正式发行版本通过自有后端代理模型服务:
```text
App → HTTPS backend → model provider
```
开发者密钥不能放入 APK。网络层只在用户主动选择 AI 解读后工作,设置合理连接/读取超时、取消传播和有限重试。不得后台预取解释,不得在用户继续编辑问题时偷偷重发。
如果未来支持用户自带密钥,它是单独的产品模式,需要 Android Keystore、清晰风险说明和独立决策记录,不能与默认发布路径混合。
## 9. 安全与隐私
- 仅请求联网所需权限;起卦不需要相机、定位、联系人、传感器或存储权限。
- 所有请求使用 TLS,服务端执行认证、限流和滥用保护。
- 日志不得包含问题原文、完整提示词、模型回复或 API 密钥。
- 崩溃与分析事件只记录枚举状态、耗时桶和匿名错误码。
- 用户删除本地记录后,不保留隐藏副本;服务端数据保留策略需在 AI 上线前单独确认。
- 任何来自 assets、数据库或网络的数据在边界处解析和校验,失败后进入有恢复路径的错误状态。
## 10. 依赖和构建政策
- 版本集中在 Gradle Version Catalog。
- 优先使用 AndroidX、Kotlin 官方组件和维护活跃的小型依赖。
- 新依赖必须说明用途、维护状态、许可证、体积和是否可由现有能力替代。
- 禁止直接引入完整“算命 SDK”或无法审计的卦象计算库。
- Compose 依赖使用 BOM 对齐版本。
- `compileSdk`/`targetSdk` 使用实现时最新稳定且满足商店要求的版本,不在方案文档硬编码会迅速过期的数值。
- `minSdk` 默认提案为 26,最终值见[决策记录](decisions.md#未决问题)。
## 11. 多模块化触发条件
满足以下至少两个条件后再评估从 `base` 迁移为多模块:
- 两名以上开发者长期并行修改独立 feature;
- 构建时间已经影响反馈循环;
- 有可独立发布/复用的设计系统或领域库;
- 历史、内容浏览、账户等功能使单模块边界难以机械约束;
- 需要独立基准测试或测试应用。
迁移前必须新增 ADR;“项目看起来更专业”不是拆模块理由。
+170
View File
@@ -0,0 +1,170 @@
# 数据、内容与隐私契约
> 状态:结构已定义,内容来源与授权仍为发布阻塞项
> 适用范围:卦库 assets、Room、DataStore、导入脚本和内容审核
## 1. 数据分类
| 数据 | 来源 | 默认位置 | 敏感性 | 是否参与起卦 |
|---|---|---|---|---|
| 三轮币面/六爻结果 | 用户输入/本地计算 | 会话状态,可选 Room | 低至中 | 是 |
| 用户问题 | 用户输入 | 会话状态,可选 Room | 高 | 否 |
| 64 卦与爻辞 | 经审核的内容包 | assets 或预置 Room | 低,关注版权 | 仅用于查表 |
| 本地解释 | 编辑内容 | assets | 低,关注版权 | 否 |
| AI 请求与回复 | 用户主动请求 | 内存,可选 Room | 高 | 否 |
| 偏好与同意版本 | 用户设置 | DataStore | 中 | 否 |
任何“否”的数据都不能成为 `CastEngine` 输入。
## 2. 本地内容包
建议使用带清单的版本化 JSON:
```json
{
"schemaVersion": 1,
"contentVersion": "zh-Hans-2026.1",
"sources": [
{
"id": "source-id",
"title": "来源名称",
"edition": "版本说明",
"license": "许可证或公版依据",
"url": "https://example.invalid"
}
],
"hexagrams": [
{
"kingWenNumber": 1,
"name": "乾",
"symbol": "䷀",
"lowerTrigram": "QIAN",
"upperTrigram": "QIAN",
"patternBottomUp": ["YANG", "YANG", "YANG", "YANG", "YANG", "YANG"],
"judgmentOriginal": "…",
"judgmentPlain": "…",
"lineTextsBottomUp": ["…", "…", "…", "…", "…", "…"],
"linePlainBottomUp": ["…", "…", "…", "…", "…", "…"],
"specialUsageText": "…",
"sourceRefs": ["source-id"]
}
]
}
```
示例中的省略号不是可发布内容。禁止由 AI 在构建时临时补齐缺失卦辞或爻辞。
## 3. 内容完整性门禁
内容包进入应用前必须自动验证:
- `schemaVersion` 为客户端支持值;
- 64 个条目数量准确;
- 文王卦号 1–64 各出现且只出现一次;
- 64 个 `patternBottomUp` 各不重复,且与上下卦一致;
- 每卦恰有六条 bottom-up 爻辞;
- 名称、卦辞、来源引用和许可证字段非空;
- 乾、坤特殊文本是否存在与内容版本声明一致;
- 所有文本为有效 UTF-8,无 HTML/Markdown 脚本或不可见控制字符;
- 生成内容校验摘要,便于安装包与内容版本对应。
运行时若内容包校验失败,应用应显示数据完整性错误并禁用结果查表,不能使用错误卦、相邻数组项或 AI 猜测作为降级。
## 4. 来源与授权
- 古代原文、公版翻译、现代译注和编辑改写必须分栏记录,不能把“古籍公版”错误外推到现代译本。
- 每段可发布文本必须能追踪到 `sourceRefs` 和授权依据。
- 现代白话建议由项目自行撰写并审核,或使用明确允许再分发和修改的来源。
- 字体、纹理、插画和音效同样进入许可证清单。
- 发布包内提供“内容来源”页面;用户能查看版本和来源。
内容负责人确认来源前,64 卦内容任务不可标记完成。
## 5. Room 数据模型提案
历史记录不是起卦事实源,但应保存可复核的快照:
```text
CastingSessionEntity
- id: UUID string, primary key
- createdAt: Instant/epoch millis
- methodVersion: string
- contentVersion: string
- coinConvention: enum string
- roundsJson: validated JSON
- lineValues: compact validated representation
- primaryHexagramId: 1..64
- transformedHexagramId: 1..64
- movingPositions: validated representation
- questionText: nullable
- questionSaved: boolean
ExplanationEntity
- id: UUID string, primary key
- sessionId: foreign key
- source: LOCAL | AI
- contractVersion: string
- content: validated structured JSON
- createdAt
ActionNoteEntity
- sessionId: foreign key
- actionText: nullable
- timeboxText: nullable
- completedState: NOT_SET | PLANNED | DONE | ABANDONED
```
约束:
- 数据库存储原始 18 枚币面和确定结果,读取时可重新计算并进行一致性检查。
- `questionText` 默认可为空;用户不保存问题时不能偷偷复制到其他表。
- 删除 session 应级联删除解释和行动记录。
- enum 使用稳定字符串或显式转换,不依赖 Kotlin ordinal。
- 每次 schema 迁移必须有 migration test;禁止发布构建使用 destructive migration。
## 6. DataStore 范围
DataStore 仅保存少量偏好:
- onboarding 版本是否已读;
- 主题:系统/浅色/深色;
- 减少应用内非必要动画;
- AI 同意文本版本和同意时间;
- 默认解释方式;
- 是否允许保存问题原文。
不要把六爻历史、完整内容包或 AI 长文本塞入 DataStore。
## 7. 隐私规则
- 问题文本被视为敏感用户内容。
- 本地核心流程不需要网络权限之外的危险权限。
- 未点击 AI 解读时,问题和卦象不得离开设备。
- AI 请求前展示准确的数据清单和服务方类别。
- 日志、分析、崩溃报告、截图测试夹具不得使用真实用户问题。
- 调试日志使用固定脱敏样例;发布构建关闭网络 body logging。
- 历史导出、云备份和 Android Auto Backup 策略在实现前必须单独决策。
## 8. 数据生命周期
```text
草稿问题/未完成投币:SavedStateHandle + 内存
↓ 用户完成
不可变 CastResult:结果页内存
↓ 用户明确保存
Room 历史快照
AI 请求:请求期间内存 → 服务端按已披露策略处理
AI 回复:结果页内存 → 用户保存后可进入 Room
```
会话中止后是否保留草稿由产品设置决定;无论如何都不能把草稿作为遥测发送。
## 9. 内容变更规则
- 修正文案:提升 `contentVersion`,记录来源和审校人。
- 修改 schema:提升 `schemaVersion`,提供向后兼容或明确迁移。
- 修改领域算法:提升 `methodVersion`,不得通过内容版本掩盖。
- 历史页必须按记录中的内容/方法版本解释旧结果,或明确提示当前展示使用了新版内容;不能静默伪装成原始解释。
Android 存储选择参考:[数据与文件概览](https://developer.android.com/training/data-storage/)、[Room 预置数据库](https://developer.android.com/training/data-storage/room/prepopulate)。
+125
View File
@@ -0,0 +1,125 @@
# 架构决策与未决问题
> 状态:持续维护
> 规则:已接受决定不得被实现者静默推翻;变更需新增记录并说明迁移影响
## 决策状态
- `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:历史记录采用明确保存、本机优先
- 状态:`Proposed`
- 日期:2026-08-04
- 决定:用户完成后明确保存才进入 Room;保存时可不保留问题原文;默认无账号和云同步。
- 原因:问题可能高度敏感,自动永久保存不是安全默认值。
- 后果:历史功能不能成为完成起卦的前置条件。
## ADR-010:多动爻全部透明展示
- 状态:`Accepted`
- 日期:2026-08-04
- 决定:显示所有实际动爻及其文本,不采用未经产品确认的规则隐藏或只选一条“主爻”。
- 原因:不同解释传统存在差异,产品不应把一种裁决算法伪装成唯一事实。
- 后果:AI 可以组织内容,但输入和界面保留全部动爻。
## 未决问题
| ID | 问题 | 推荐默认 | 阻塞阶段 |
|---|---|---|---|
| TBD-001 | 正式产品名与应用图标 | Brainwave 仅作代码代号 | P0 商店配置 |
| TBD-002 | application ID | 使用组织所有的反向域名 | P0 |
| TBD-003 | minSdk | 26;创建工程时复核覆盖率和依赖要求 | P0 |
| TBD-004 | 问题是否允许留空 | 允许选择“不写具体内容”,但需显式操作 | P3 |
| TBD-005 | 经典原文、现代白话的版本与授权 | 自有白话 + 可核验公版原文 | P2,发布阻塞 |
| TBD-006 | 乾用九、坤用六是否纳入 MVP | 内容具备时展示 | P2/P3 |
| TBD-007 | 历史是否默认保存卦象 | 默认不保存,完成后询问 | P4 |
| TBD-008 | Android Auto Backup 是否包含历史 | 默认排除敏感历史,待隐私评审 | P4 |
| TBD-009 | AI 模型供应商与自有后端 | 供应商无关接口;先交付本地版 | P5 |
| TBD-010 | 服务端问题/回复保留期 | 最小化且明确披露,优先不持久化正文 | P5,发布阻塞 |
| TBD-011 | 高风险本地资源表覆盖地区 | 首发市场确认后维护,不让模型编号码 | P5 |
| TBD-012 | 架构检查工具 | 选择维护活跃工具或小型自定义测试 | P0/P1 |
## 新增决策模板
```markdown
## ADR-NNN:标题
- 状态:Proposed | Accepted | Superseded | Rejected
- 日期:YYYY-MM-DD
- 关联:需求、issue 或旧 ADR
- 决定:一句可执行结论
- 原因:为什么现在这样选择
- 备选:认真考虑过什么
- 后果:实现、迁移、测试和文档影响
- 复审条件:何时重新评估
```
不要直接改写旧 ADR 来隐藏历史。需要反转时新增 ADR,并把旧记录标为 `Superseded by ADR-NNN`。
+163
View File
@@ -0,0 +1,163 @@
# 领域规则:三枚铜币与六爻
> 状态:MVP 强制规范
> 适用范围:所有起卦算法、显示模型、持久化模型和相关测试
本文件定义本项目唯一允许的计算规则。UI 文案、AI 输出和数据源都不能覆盖这些规则。
## 1. 术语和类型
建议使用有语义的封闭类型,避免裸 `Int` 在层间传播:
```kotlin
enum class CoinSide(val value: Int) {
CHARACTER(2), // 字面
REVERSE(3), // 背面
}
enum class LineValue(val sum: Int, val polarity: Polarity, val moving: Boolean) {
OLD_YIN(6, Polarity.YIN, true),
YOUNG_YANG(7, Polarity.YANG, false),
YOUNG_YIN(8, Polarity.YIN, false),
OLD_YANG(9, Polarity.YANG, true),
}
enum class Polarity { YIN, YANG }
```
代码片段只表达契约,最终实现的包名和细节以[系统架构](architecture.md)为准。
## 2. 铜币计值约定
MVP 固定采用:
- 字面 `CHARACTER` 计 2;
- 背面 `REVERSE` 计 3;
- 每轮三枚相加,结果只可能是 6、7、8、9。
传统资料对“正/反”“阴/阳”与 2/3 的命名存在不同约定,因此界面不得只写含糊的“正面/反面”。首次使用和投币页必须始终显示当前计值:“字 2,背 3”。如果未来允许切换约定,约定必须在第一轮之前锁定并写入结果;不得在六轮中途改变。
## 3. 爻值映射
| 和值 | 名称 | 本卦爻形 | 是否动爻 | 之卦爻形 |
|---:|---|---|---|---|
| 6 | 老阴 | 阴爻,断线 | 是 | 阳爻,实线 |
| 7 | 少阳 | 阳爻,实线 | 否 | 阳爻,实线 |
| 8 | 少阴 | 阴爻,断线 | 否 | 阴爻,断线 |
| 9 | 老阳 | 阳爻,实线 | 是 | 阴爻,断线 |
强制不变量:
- 偶数 6、8 为阴;奇数 7、9 为阳。
- 只有 6 和 9 为动爻。
- 之卦只翻转动爻;静爻保持不变。
- AI、网络、时间、问题文本和设备状态均不得影响上述映射。
## 4. 六次录入与顺序
数组和数据库中的标准顺序一律为 **bottom-up**:
| 数组索引 | 中文位置 | 投币轮次 |
|---:|---|---:|
| 0 | 初爻 | 1 |
| 1 | 二爻 | 2 |
| 2 | 三爻 | 3 |
| 3 | 四爻 | 4 |
| 4 | 五爻 | 5 |
| 5 | 上爻 | 6 |
绘制界面时可以从屏幕顶部先画上爻,但只能在展示适配层反转;领域对象、序列化数据和测试夹具不得改成 top-down。
一个有效投币过程必须满足:
- 恰好六轮;
- 每轮恰好三枚;
- 每枚只能是 `CHARACTER` 或 `REVERSE`;
- 生成结果后保留原始 18 枚输入,便于复核和审计。
## 5. 本卦与之卦
`CastResult` 至少包含:
```text
methodVersion
coinConvention
rounds[6][3]
lineValuesBottomUp[6]
primaryPatternBottomUp[6]
movingLinePositions[] // 1..6,升序
transformedPatternBottomUp[6]
primaryHexagramId // 文王卦序 1..64
transformedHexagramId // 文王卦序 1..64
contentVersion
createdAt // 只用于记录,不参与计算
```
卦号不能通过“二进制数 + 1”推断。文王卦序不是简单二进制顺序,必须使用经过校验的“上卦 × 下卦”映射表或完整六爻模式映射表。
算法顺序:
1. 将每轮三枚计值相加为 `LineValue`。
2. 按录入顺序形成 bottom-up 六爻。
3. 从各爻阴阳形成本卦模式并查得本卦编号。
4. 收集值为 6 或 9 的位置作为动爻。
5. 仅翻转动爻阴阳,形成之卦模式并查得之卦编号。
6. 将全部原始输入、版本和确定结果构造成不可变 `CastResult`。
## 6. 读取内容的产品规则
MVP 采用透明而不过度裁决的显示策略:
- 始终显示本卦卦辞。
- 无动爻:明确显示“无动爻”;之卦与本卦相同,可弱化重复内容。
- 有动爻:按初爻到上爻显示本卦中所有实际动爻的爻辞,并显示之卦。
- 多个动爻:不由算法擅自选出“唯一主爻”,也不隐藏其他动爻。
- 乾卦六爻皆九、坤卦六爻皆六时,若采用的数据版本含“用九/用六”,应作为特殊文本额外展示;是否纳入 MVP 见[决策记录](decisions.md#未决问题)。
AI 可以组织和解释这些材料,但不得改变显示集合。
## 7. 状态机
```text
DRAFT(question)
└─ start → CASTING(confirmedRounds = 0..5)
├─ edit previous → CASTING
├─ confirm sixth → SEALED(CastResult)
└─ cancel → DRAFT
SEALED
├─ request local explanation → EXPLAINED_LOCAL
├─ consent + request AI → EXPLAINING_AI → EXPLAINED_AI | AI_FAILED
└─ explicit restart → DRAFT(newSessionId)
```
禁止从 `EXPLAINING_AI` 或 `EXPLAINED_AI` 回写 `CastResult`。重新起卦必须创建新的会话标识。
## 8. 必须存在的确定性测试
| 输入(初爻到上爻) | 本卦 | 之卦 | 动爻 |
|---|---|---|---|
| `7,7,7,7,7,7` | 乾 1 | 乾 1 | 无 |
| `8,8,8,8,8,8` | 坤 2 | 坤 2 | 无 |
| `9,9,9,9,9,9` | 乾 1 | 坤 2 | 1–6 |
| `6,6,6,6,6,6` | 坤 2 | 乾 1 | 1–6 |
| `7,8,8,8,8,8` | 复 24 | 复 24 | 无 |
| `9,8,8,8,8,8` | 复 24 | 坤 2 | 初爻 |
此外必须:
- 穷举 8 种三枚铜币排列,验证求和与 `LineValue`。
- 穷举 4⁶ = 4,096 种六爻数值组合,验证长度、动爻和变换不变量。
- 验证 64 种阴阳模式恰好映射到 64 个不重复卦号。
- 验证序列化再反序列化不改变任何确定字段。
## 9. 版本规则
- 初始算法版本建议为 `coin-v1`。
- 改变字/背计值、动爻规则、顺序或卦号映射都属于破坏性领域变更,必须新增版本和决策记录,不能静默覆盖历史结果。
- 内容措辞变化只提升 `contentVersion`,不得改变 `methodVersion`。
参考资料仅用于交叉核验;仓库内上述规则才是实现事实源:
- [三枚铜币产生 6/7/8/9,并自下而上记录](https://uaya.org/learn/iching/using-the-oracle/casting-techniques/three-coins/understanding-results/)
- [三枚铜币法的爻值与动爻说明](https://www.ichingonline.net/instruction.php)
+71
View File
@@ -0,0 +1,71 @@
# 失败记忆与防回归台账
> 状态:初始风险基线;实现和运营过程中持续追加
> 用途:把容易重复的错误转化为持久约束和机械反馈
## 1. 已知高风险模式
| ID | 失败模式 | 常见症状 | 持久护栏 | 验证 |
|---|---|---|---|---|
| FM-001 | 六爻顺序被反转 | 已知输入显示成另一卦;初爻画在顶部 | 领域统一 bottom-up,仅展示适配层反转 | 已知夹具 + 4,096 组合测试 + UI 语义测试 |
| FM-002 | 6/7/8/9 映射错误 | 6/9 未变或 7/8 被标成动爻 | `LineValue` 封闭枚举,禁止散落 `% 2`/magic number | 8 种币面与映射参数化测试 |
| FM-003 | 用二进制序号冒充文王卦序 | 阴阳形正确但卦号/卦名错误 | 经审核的完整模式映射表 | 64 模式唯一性与已知卦测试 |
| FM-004 | UI top-down 数据回流领域层 | 保存后再打开结果颠倒 | DTO 字段名强制 `BottomUp`,序列化往返测试 | Room/DTO round-trip |
| FM-005 | AI 参与或改写起卦 | AI 前后卦号变化;网络失败无结果 | `CastEngine` 纯 Kotlin;解释状态与结果分离;依赖检查 | 请求前后不可变测试、离线 E2E |
| FM-006 | 用户未点击就发起 AI 请求 | 进入结果页即出现网络流量 | 显式调用门与同意状态机 | fake server 调用次数为 0 |
| FM-007 | 生产密钥进入 APK | BuildConfig/strings/NDK 中出现 key | 后端代理、secret scan、APK 检查 | CI secret scan + release artifact scan |
| FM-008 | 敏感问题进入日志 | crash/HTTP 日志出现原文 | 结构化脱敏错误码;发布关闭 body logger | 日志捕获测试与人工抓取 |
| FM-009 | 内容缺失时数组错位 | 第 N 卦显示第 N+1 卦文本 | 以显式卦号查表;启动/构建期 schema 校验 | 缺失/重复条目负向测试 |
| FM-010 | 使用未授权现代译文 | 上架投诉或无法说明来源 | 每段 `sourceRefs` + 许可证清单 + 发布签核 | 内容校验 + 人工版权审核 |
| FM-011 | “国风”装饰损害可用性 | 低对比水墨、毛笔正文、小铜币按钮 | 语义令牌、48dp、对比度和反模式清单 | accessibility test + 真机评审 |
| FM-012 | 动爻只用朱砂色表示 | 色觉用户/读屏无法识别 | 颜色 + 形状/符号 + 文本 | TalkBack 和去色检查 |
| FM-013 | 旋转或进程重建重复提交 | 丢轮次、重复 AI 扣费 | SavedState、idempotency key、显式请求状态 | 重建/并发/取消测试 |
| FM-014 | AI 输出被当 HTML/命令执行 | 恶意链接、样式或脚本进入 UI | 结构化纯文本 schema、长度和字符校验 | 对抗性响应测试 |
| FM-015 | 高风险问题得到命令式答案 | 模型要求买卖、停药、立即分手 | 服务端安全提示、测试集、本地降级 | 高风险金丝雀用例 |
| FM-016 | 为通过测试关闭门禁 | ignored test、宽泛 catch、destructive migration | DoD 与评审规则,失败必须归因 | CI 检查 skipped tests/配置差异 |
| FM-017 | 文档与实现漂移 | 代理按旧命令/旧结构工作 | 文档状态、链接检查、行为变更同提交 | CI docs check + 里程碑熵清理 |
| FM-018 | 将实时 AI 当唯一测试 oracle | 测试不稳定、成本和输出漂移 | fake server + 固定 schema fixtures | JVM/integration tests 无外网依赖 |
## 2. 故障登记模板
可复现且有再次发生价值的故障追加如下记录:
```markdown
## INCIDENT-YYYYMMDD-NN:短标题
- 发现日期:
- 影响需求:
- 环境/版本:
- 用户症状:
- 最小复现:
- 根因:
- 为什么旧护栏没捕获:
- 修复:
- 新增测试/静态规则:
- 相关提交或 issue:
- 后续观察:
```
不要记录密钥、真实用户问题或完整 AI 回复;使用脱敏夹具。
## 3. 提升规则
- 同类问题第一次出现:修复并加回归测试。
- 第二次出现:在本文件登记,并优先增加 lint、架构测试、schema 或聚合验证。
- 影响领域确定性、隐私、密钥、内容授权或高风险 AI 的问题:第一次即登记并加入发布门禁。
- 只写“以后注意”不算护栏;必须指出由什么测试、脚本、类型或评审入口阻止复发。
## 4. 定期审计问题
每个里程碑检查:
1. 是否出现新的裸数字 2/3/6/7/8/9 业务逻辑?
2. 是否有非 `CastEngine` 代码重新推导卦象?
3. 是否有新增网络路径在用户同意前发送内容?
4. 是否有日志或测试夹具包含类似真实问题的文本?
5. 是否有卦辞、字体、图片缺少来源?
6. 是否有页面绕过主题令牌或无障碍语义?
7. 是否有依赖方向、测试命令或文档链接已经过期?
8. 是否有多次出现但仍只靠人工评审发现的问题?
审计输出应形成小型、可评审的修复任务,而不是一次大规模无边界重写。
+196
View File
@@ -0,0 +1,196 @@
# 分阶段实施计划
> 状态:未开始
> 计划原则:先锁定确定性领域核心,再接内容和 UI,最后接网络 AI
## 1. 依赖图
```text
P0 工程骨架
├── P1 起卦领域核心 ──→ P3 主流程 UI ──→ P4 本地完整 MVP
├── P2 内容数据管线 ──→ P3 主流程 UI
└── P6 质量与发布(贯穿)
P4 本地完整 MVP ──→ P5 AI 后端与解释
```
P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临时算法或硬编码卦辞。
## 2. P0:仓库与 Android 骨架
目标:建立可构建、可测试、可导航的原生 Android 项目。
任务:
- 基于 Google `android/architecture-templates` 的 `base` 分支初始化。
- 确认正式应用名称、package/application ID、minSdk。
- 配置 Kotlin、Compose、Material 3、Hilt、Room、DataStore、Navigation 和 Version Catalog。
- 配置 Gradle Wrapper、格式化、lint、单元测试和 CI。
- 建立 [系统架构](architecture.md)中的包结构和空 feature 边界。
- 将根 `AGENTS.md` 设计为短地图,指向本目录和验证命令。
- 增加 `verifyLocal` 聚合任务及基础 secret scan。
退出条件:
- Windows 上一条命令可执行格式、lint、单测和 debug 构建。
- CI 使用同一组命令。
- 空应用可在模拟器启动,导航到占位欢迎页。
- 没有生产服务密钥或真实内容。
## 3. P1:领域核心
目标:在纯 Kotlin 中完成并证明三枚铜币算法。
任务:
- 建立 `CoinSide`、`LineValue`、`Polarity`、`CastRound`、`CastResult`。
- 实现六轮输入校验和 `CastEngine`。
- 建立经过双重校验的 64 卦模式映射表。
- 实现之卦变换和动爻位置。
- 实现版本化序列化 DTO。
- 完成 8 种币面、4,096 种六爻、64 模式和已知夹具测试。
- 增加 domain 无 Android/网络依赖的架构门禁。
退出条件:
- [领域规则](domain-rules.md)全部被测试覆盖。
- 测试无随机、无网络、无系统时间依赖。
- `CastEngine` API 经评审后冻结为 `coin-v1`。
## 4. P2:内容数据管线
目标:建立可追踪、可校验、可发布的本地内容包。
任务:
- 决定原文版本、现代白话来源和授权。
- 实现 JSON schema、解析器和内容版本。
- 录入/导入 64 卦、卦辞、384 条爻辞及所需特殊文本。
- 建立来源清单、许可证清单和内容审核记录。
- 实现构建期完整性校验与映射交叉校验。
- 实现 `HexagramContentRepository` fake 与 assets 版本。
退出条件:
- 64 卦/384 爻数据完整、唯一且来源可追踪。
- 缺失、重复、非法顺序和错误映射测试均能失败。
- 内容负责人确认可再分发。
## 5. P3:核心用户流程与东方设计系统
目标:完成离线起念、六次录入和结果阅读。
任务:
- 实现颜色、字体、间距、形状和动画令牌。
- 实现卦象 `Canvas`、动爻标记和读屏语义。
- 实现首次说明、起念、投币和结果页面。
- 实现 `CastingSessionViewModel` 状态机及 SavedState 恢复。
- 支持前五轮返回修改、第六轮封印和明确重新起卦。
- 接入本地内容并区分原文/本地白话。
- 完成深浅主题、字体缩放、TalkBack、横屏和大屏适配。
退出条件:
- 飞行模式可以从起念走到完整结果。
- 已知夹具的屏幕卦象、名称、动爻和之卦一致。
- AI/网络代码尚未存在也不影响流程。
- [UX 与东方视觉](ux-design.md)检查表通过。
## 6. P4:本地解释与历史
目标:形成不依赖 AI 的完整 MVP。
任务:
- 实现版本化本地解释模板。
- 实现“可以试的一小步”的非裁决式结构。
- 实现 Room 历史、可选保存问题、详情和删除。
- 实现 DataStore 设置与同意版本基础设施。
- 完成 migration、删除、隐私和离线测试。
退出条件:
- 用户可选择不保存问题而保存卦象结果。
- 本地解释在无网络时可用。
- 删除行为与备份策略一致且经过验证。
- 此阶段已经是可发布的本地版候选。
## 7. P5:AI 解读
目标:在不扩大起卦权限的前提下增加可控 AI 解释。
前置阻塞:
- 模型提供商和后端部署方案确定;
- 隐私政策、数据保留和成本/限流规则确认;
- AI 安全用例集通过产品审核。
任务:
- 建立后端代理、认证、限流、超时和脱敏日志。
- 实现 `explanation-v1` 请求/响应 schema 与提示词版本。
- Android 实现同意、请求、取消、重试和本地降级。
- 对提示注入、高风险问题、非法输出和模型故障做测试。
- 增加远程功能开关;关闭 AI 时本地版仍完整。
退出条件:
- [AI 解释与安全](ai-safety.md)所有验收测试通过。
- APK 无生产模型密钥。
- 抓包显示只有用户明确动作触发请求,载荷与同意说明一致。
- 服务端故障不会改变或隐藏 `CastResult`。
## 8. P6:质量、发布与运营
贯穿所有阶段:
- 建立 CI、架构检查、内容校验、secret scan 和依赖许可证报告。
- 建立脱敏崩溃监控和最小匿名指标。
- 增加可复现 screenshot/accessibility 测试环境。
- 准备隐私政策、内容来源、免责声明和应用商店素材。
- 每次重复缺陷更新[失败记忆](failure-memory.md)和机械护栏。
- 定期清理未使用依赖、重复 helper、过期文档和 TODO。
发布条件完全遵循[质量门禁](quality-gates.md#6-发布门禁)。
## 9. 编码任务模板
后续任务应使用以下结构,减少代理猜测:
```markdown
## 目标
一个可验证的结果。
## 范围
- 允许修改:...
- 不在范围:...
## 需求
- FR-C-004
- NFR-DET-001
## 必读
- docs/domain-rules.md
- docs/architecture.md
## 验收
- Given/When/Then 场景
- 要运行的具体命令
## 风险
- 数据迁移 / 隐私 / 内容授权 / 无障碍 / 无
```
任务应尽量小到单次变更可完整验证,不以“大致完成页面”作为验收描述。
## 10. MVP 切分建议
最稳妥的发布顺序:
1. **内部算法版**:P0–P1,仅验证领域核心。
2. **离线体验版**:P2–P3,给测试用户完成起卦与阅读。
3. **本地正式候选**:P4,不依赖 AI 即可发布。
4. **AI 增强版**:P5,通过后由功能开关逐步开放。
这样 AI、后端或供应商选择不会阻塞核心产品,也不会迫使客户端把密钥和高风险逻辑提前塞入 APK。
+143
View File
@@ -0,0 +1,143 @@
# 产品规格
> 状态:MVP 方案基线
> 产品代号:Brainwave(正式名称 TBD)
> 原始输入:[原始需求.txt](原始需求.txt)
## 1. 产品定义
Brainwave 帮助用户把一件正在纠结的事放慢来看。应用保留三枚铜币、六次成卦、本卦、之卦和动爻的文化结构,但把输出定位为反思材料,而不是预测、命令或替代决策。
核心承诺:
1. 卦由用户的真实投币结果在本地确定。
2. AI 只解释已经确定的结果,无法参与、重算或改写起卦。
3. 用户在看到原始结果后,主动点击「解」才会触发解释。
4. 解释尽量落到一项现实、低风险、可撤销的小行动。
5. 应用不预测发财时间,不替用户决定辞职、分手、医疗、法律或投资行为。
## 2. 产品原则
| 原则 | 产品含义 |
|---|---|
| 人先于算法 | 用户亲手投币;应用只记录和计算 |
| 原文先于解释 | 先展示本卦、之卦、卦辞和动爻,再提供「解」 |
| 反思而非裁决 | 使用“可以观察/考虑”,不用“你必须/注定” |
| 本地优先 | 无网络时仍可完成输入、起卦、查看本地内容 |
| 明示边界 | AI 调用、数据发送、来源和失败状态都对用户可见 |
| 克制表达 | 东方文化氛围服务于阅读和节奏,不制造神秘权威 |
## 3. 目标用户与使用情境
主要用户是希望通过传统文化框架整理想法、但不希望被“算命结果”替代判断的中文 Android 用户。
典型情境:
- 在两个可行选择间犹豫,希望换一个角度观察。
- 情绪复杂时,先慢下来描述问题,再决定下一步。
- 对《易经》文化感兴趣,希望理解卦象和动爻的基本结构。
- 想保存一次反思过程,日后回看当时的判断与行动。
本产品不适合作为紧急求助、医疗诊断、法律意见、投资建议或人身安全决策工具。
## 4. MVP 用户流程
```text
首次说明
↓
起念:写下纠结之事
↓
定法:确认“背=3、字=2”和由下而上
↓
投币:真实投三枚,逐次录入,共六次
↓
成卦:本地锁定本卦、之卦、动爻
↓
观象:查看卦名、卦辞、动爻原文/本地白话
↓
主动点击「解」
↓
本地解释或明确同意后调用 AI
↓
留下一项低风险、可撤销的小行动
```
用户可以在前五次录入期间返回修改;第六次确认后生成不可被 AI 修改的 `CastResult`。若要改变投币记录,必须明确选择“重新起卦”,旧结果不在原地静默变化。
## 5. 功能需求
### 5.1 起念
- **FR-Q-001** 应用允许用户输入当前纠结的问题,建议长度 1–200 个 Unicode 字符。
- **FR-Q-002** 问题字段必须有可见标签和隐私说明,不能仅使用占位文字。
- **FR-Q-003** MVP 不要求账号、手机号或身份信息。
- **FR-Q-004** 问题在本地保存与否必须由用户可见的设置或保存动作决定,不得默认上传。
### 5.2 投币与成卦
- **FR-C-001** 用户每轮录入三枚铜币的“字/背”,共六轮。
- **FR-C-002** 应用不提供随机、AI 或服务器代投功能。
- **FR-C-003** 每轮即时显示该爻的数值和阴阳/动静含义,但不得提前生成完整卦名来影响后续录入。
- **FR-C-004** 六爻按初爻至上爻、由下而上存储和绘制。
- **FR-C-005** 第六轮确认后,本地生成稳定、可序列化的 `CastResult`。
- **FR-C-006** 投币、求和、动爻变换和卦号映射必须符合[领域规则](domain-rules.md)。
- **FR-C-007** 进程重建或屏幕旋转不得悄悄丢失已确认的投币进度。
### 5.3 结果展示
- **FR-R-001** 结果页必须显示六条原始爻、本卦名称和编号。
- **FR-R-002** 有动爻时必须显示动爻位置、动爻文本和之卦;无动爻时明确显示“无动爻”。
- **FR-R-003** 动爻不能只靠颜色区分,还必须有文字或形状标记。
- **FR-R-004** AI 解释加载失败不得影响原始结果和本地内容的展示。
- **FR-R-005** 本卦与之卦的内容必须来自版本明确、授权清晰的本地数据集。
### 5.4 解释
- **FR-E-001** 结果锁定且原始内容已经可见后,才显示可用的「解」操作。
- **FR-E-002** 用户可以选择本地解释;AI 是可选增强,不是完成流程的前提。
- **FR-E-003** 第一次发送给 AI 前,必须说明会发送哪些内容并获得明确同意。
- **FR-E-004** AI 输出必须符合[AI 解释与安全](ai-safety.md)的结构和语言边界。
- **FR-E-005** 解释必须将本卦、之卦和动爻视为输入事实,不得重新生成或纠正它们。
- **FR-E-006** 最终行动建议必须低风险、可撤销、有限时,并保留“不采取行动”的选项。
### 5.5 历史记录
- **FR-H-001** 历史记录属于 MVP 后半段能力;核心起卦不依赖它。
- **FR-H-002** 保存时必须允许用户选择是否保留问题原文。
- **FR-H-003** 删除记录必须可撤销或经过确认。
- **FR-H-004** 未获得单独授权时,历史记录只保存在应用私有存储中。
## 6. 非功能需求
- **NFR-OFF-001** 写问题、录入、起卦、查看本地卦辞在飞行模式下可用。
- **NFR-DET-001** 相同的六次投币输入和同一数据版本必须产生完全相同的本卦、动爻和之卦。
- **NFR-SEC-001** 开发者 AI 密钥不得进入 APK、源码仓库或客户端日志。
- **NFR-PRI-001** 问题文本、解释全文和模型请求不得写入分析埋点或崩溃日志。
- **NFR-A11Y-001** 正文对比度至少 4.5:1;交互目标至少 48×48dp;支持系统字体缩放和读屏。
- **NFR-PERF-001** 本地起卦为纯内存同步计算,用户确认后应即时完成,不显示伪加载动画。
- **NFR-RES-001** 旋转、切后台和可恢复的进程重建后保留当前流程状态。
- **NFR-I18N-001** 首版以简体中文为基线,所有用户文案从资源文件读取,不硬编码在 Composable 中。
## 7. MVP 非目标
- 自动、摇手机或 AI 随机起卦。
- 根据日期、位置、设备传感器或用户画像改变卦象。
- 社区、排行榜、连续签到、灵验率或“改善运势”机制。
- 付费解锁“更准”结果、制造焦虑的倒计时或重复付费抽取。
- 医疗、法律、投资、博彩、婚恋或职业决定的确定性建议。
- 用 AI 自动决定应该读取哪一爻或隐藏用户已经得到的动爻。
- 首版账号体系、云同步和社交分享图生成。
## 8. 成功标准
MVP 可以交付的最低条件:
1. 新用户不阅读帮助也能正确完成六次录入。
2. 所有 4,096 种六爻数值组合均能确定本卦和之卦,且满足领域不变量。
3. 断网时完整完成本地流程。
4. AI 被禁用、超时或返回非法内容时,原始结果仍然完整且可阅读。
5. 用户能清楚分辨“经典/本地内容”“AI 解释”和“建议行动”。
6. 无障碍检查覆盖投币控件、卦象、动爻和长文阅读。
7. 64 卦及爻辞内容通过完整性、来源和授权门禁。
需求与验证方式的对应关系见[质量门禁](quality-gates.md#4-需求追踪矩阵)。
+156
View File
@@ -0,0 +1,156 @@
# 质量门禁与验证策略
> 状态:测试策略已定义;命令在 Android 骨架初始化后启用
> 原则:完成必须有可重复证据,不能以“代码看起来正确”代替验证
## 1. 反馈循环
每个实现任务遵循:
```text
需求编号 → 最小实现 → 就近测试 → 全量静态/单元检查 → 设备验证 → 文档同步
↑ ↓
└──── 失败归因与修复 ────┘
```
同类失败第二次出现时,应更新[失败记忆](failure-memory.md);适合机械检查的规则必须转成测试、lint 或脚本。
## 2. 预期本地命令
工程创建后,Windows 环境至少提供以下稳定入口:
```powershell
.\gradlew.bat spotlessCheck
.\gradlew.bat lintDebug
.\gradlew.bat testDebugUnitTest
.\gradlew.bat assembleDebug
.\gradlew.bat connectedDebugAndroidTest
```
若采用不同格式化插件,命令可以调整,但必须在本文件和 CI 同步更新。`connectedDebugAndroidTest` 需要模拟器或设备,应与纯 JVM 快速门禁分开。
建议再提供聚合任务:
```powershell
.\gradlew.bat verifyLocal
```
它至少依赖格式、lint、JVM 单元测试和 debug 构建,使代理不必猜测正确验证组合。
当前仓库没有 Gradle Wrapper,所以上述命令尚未运行,也不能报告为通过。
## 3. 测试层次
### 3.1 纯 JVM 领域测试(最快,阻塞合并)
- 三枚铜币 8 种排列映射。
- 六爻 4,096 种组合不变量。
- 64 卦模式映射唯一性。
- bottom-up 顺序和显示适配。
- `CastResult` 序列化往返。
- 本地内容 schema 与完整性。
- 本地解释模板选择。
- AI DTO schema、长度和纯文本校验。
### 3.2 ViewModel/Repository 测试(阻塞合并)
- 投币状态机、返回修改和第六次封印。
- SavedState 恢复。
- 只在用户点击且同意后调用 AI。
- 并发点击、取消和超时不重复提交。
- Room 保存/读取一致性。
- 删除与撤销/确认行为。
测试优先使用接口的 fake 实现,不依赖真实网络和实时模型。
### 3.3 Compose UI 测试(阻塞发布)
- 首次说明与方法约定可达。
- 六轮录入的进度、按钮启用状态和错误恢复。
- 结果页本卦/之卦/动爻语义。
- AI 同意、加载、失败、本地降级。
- 最大字体下关键操作可到达。
- TalkBack 语义、焦点顺序和触控目标。
- 旋转/横屏/大屏布局。
### 3.4 端到端与人工验收(阻塞发布)
- 飞行模式走完整核心流程。
- 真机上检查输入法、返回手势、深浅主题和触觉。
- 使用已知六爻夹具人工核对卦象绘制和文本。
- 后端 staging 环境验证同意、超时、限流、非法响应和安全分支。
- 验证安装包不包含生产密钥和未授权内容。
## 4. 需求追踪矩阵
| 需求 | 主要自动化证据 | 人工证据 |
|---|---|---|
| FR-Q-001~004 | ViewModel + Compose 输入测试 | 中文输入法、隐私文案 |
| FR-C-001~003 | 状态机与 UI 测试 | 真实投币录入可理解性 |
| FR-C-004~006 | 领域穷举与映射测试 | 已知卦象目视复核 |
| FR-C-007 | SavedState/重建测试 | 旋转、切后台、进程恢复 |
| FR-R-001~003 | UI 语义与 screenshot 测试 | TalkBack、色觉与长文阅读 |
| FR-R-004 | 网络失败测试 | 飞行模式 |
| FR-R-005 | 内容 schema/授权清单检查 | 内容负责人签核 |
| FR-E-001~003 | 网络调用次数与同意状态测试 | 首次同意流程 |
| FR-E-004~006 | 输出 schema + 安全用例集 | 安全/产品审核 |
| FR-H-001~004 | Room、删除、权限边界测试 | 隐私设置与删除体验 |
| NFR-OFF-001 | fake/offline 集成测试 | 飞行模式真机 |
| NFR-DET-001 | 4,096 组合属性测试 | 无 |
| NFR-SEC/PRI | secret scan、日志测试 | APK/代理抓包复核 |
| NFR-A11Y-001 | Compose accessibility checks | TalkBack、最大字体 |
| NFR-RES-001 | 重建和恢复测试 | 厂商设备抽测 |
## 5. 架构与数据门禁
工程初始化时应新增可机械执行的规则:
- domain 包不得依赖 Android、Compose、Room 和网络包。
- `data.ai` 不得依赖或调用 `CastEngine`。
- feature UI 不得引用 DAO 或网络 DTO。
- 生产源码不得出现 API key 形态、真实用户问题测试样例或 HTTP body logger。
- 内容校验任务验证 64 卦、384 条爻辞、唯一模式、来源和许可证字段。
- 文档链接和需求编号无悬空引用。
可以使用现有静态工具、架构测试库或小型自定义 Gradle 任务;具体选型写入[决策记录](decisions.md)。规则的错误消息应告诉代理如何修复,而不只报告失败。
## 6. 发布门禁
发布候选必须满足:
- [ ] 格式、lint、JVM 测试、UI 测试和 release 构建全部通过。
- [ ] 64 卦内容校验与授权清单通过。
- [ ] 飞行模式核心流程通过。
- [ ] AI 安全测试集和故障降级通过;若 AI 未上线则功能关闭且不影响本地流程。
- [ ] 无生产密钥、敏感日志和真实用户内容进入 APK/测试产物。
- [ ] 浅色/深色、小屏/大屏、最大字体、TalkBack、减少动态效果完成抽测。
- [ ] 隐私政策和应用内数据说明与实际网络行为一致。
- [ ] 数据库迁移、备份策略和删除行为已验证。
- [ ] 已知阻塞缺陷为 0,非阻塞缺陷有记录和责任人。
## 7. 完成定义(Definition of Done)
一个任务只有在以下条件全部满足时才算完成:
1. 关联明确的需求或缺陷编号。
2. 实现遵守领域、架构、UX 和 AI 边界。
3. 新行为有自动化测试,缺陷有回归测试。
4. 执行了与风险相称的验证命令并记录结果。
5. 失败不是通过跳过测试、降低断言或吞异常来“解决”。
6. 用户可见行为或契约变化已经同步文档。
7. 没有引入秘密、未授权内容和敏感测试数据。
8. 交付说明列出变更、验证证据、剩余风险和未执行项。
## 8. 验证报告模板
```markdown
### 验证
- 需求:FR-C-004, FR-C-006
- 已运行:`.\gradlew.bat testDebugUnitTest`
- 结果:通过,N tests
- 未运行:`connectedDebugAndroidTest`(原因:无可用模拟器)
- 人工检查:输入 9,8,8,8,8,8,得到复 24 → 坤 2,初爻动
- 剩余风险:无 / 明确列出
```
不得把未运行写成“应当通过”。
+197
View File
@@ -0,0 +1,197 @@
# UX 与东方视觉规范
> 状态:MVP 设计基线
> 适用范围:Android 手机优先,兼顾横屏、平板和系统无障碍设置
## 1. 体验定位
界面要让用户感到“安静、清楚、有分寸”。东方文化来自流程、文字、材料感和留白,不来自堆叠符号。
产品体验不是:
- 制造神秘权威的算命工具;
- 抽卡、开盲盒或“再测一次”的成瘾循环;
- 复刻古籍页面而牺牲现代触控和可读性。
产品体验是:
- 一段从起念、投币、观象到落事的缓慢反思流程;
- 原始文化材料与现代解释层次分明;
- 保留用户自主判断,随时可以退出、不保存或不调用 AI。
## 2. 信息架构
MVP 顶层不需要底部导航。主流程是线性的,使用单 Activity 和清晰的返回行为:
```text
欢迎/方法说明
└── 起念
└── 投币(1/6…6/6)
└── 结果
├── 本地解释
└── AI 解释(需同意)
次级入口
├── 历史(启用后)
├── 方法说明
├── 内容来源
└── 设置与隐私
```
投币流程返回时保留已经确认的轮次;从结果页返回不能导致重新计算。预测性返回手势必须与系统导航兼容。
## 3. 场景与页面契约
### 3.1 欢迎与方法说明
目的:在第一次起卦前建立边界和方法。
必须说明:
- “你投币,应用记录;AI 不参与起卦。”
- “字为 2,背为 3;第一次是初爻,由下而上。”
- “结果用于整理想法,不替你做决定。”
主操作只有一个:“开始”。“查看方法”是次级文本操作,不制造必须完成的教程轮播。
### 3.2 起念
页面标题可使用“此刻,你在为何事迟疑?”。问题输入框有固定标签“正在纠结的事”,辅文提示尽量描述事实、选择和顾虑,不要求生日、性别等无关信息。
交互规则:
- 多行输入,支持系统输入法和字体缩放;
- 离开页面时在会话内保留草稿;
- 默认不把问题发送到网络;
- 用户可选择“不写具体内容,直接开始”,最终是否允许空值见[决策记录](decisions.md#未决问题)。
### 3.3 投币
页面始终显示:
- 当前轮次,如“第三爻 · 3/6”;
- 三个独立的铜币录入控件;
- “字 · 2 / 背 · 3”的当前约定;
- 已确认的爻从下向上累积显示;
- 一个主按钮“确认这一爻”。
铜币控件不得仅靠拟真图片表达正反面;图形旁必须有“字/背”文本和选中语义。三个控件触控区域均不小于 48×48dp。
每次确认可以有一次轻触觉反馈和 150–250ms 的爻线出现动画。不得使用持续摇晃、金币飞散、音效倒计时或强制等待。开启“减少动态效果”时直接更新状态。
### 3.4 成卦与观象
信息优先级:
1. 本卦卦象、编号和名称;
2. 动爻位置;
3. 之卦(若与本卦不同);
4. 卦辞与实际动爻文本;
5. 「解」按钮。
卦象使用 Compose `Canvas` 或稳定的 Compose 图元绘制,不使用包含文字的位图。无障碍描述示例:“第 24 卦,复;初爻阳,其余为阴;初爻为动爻”。
原文、本地白话和 AI 解释必须有明确标签,不使用视觉相似但来源不明的段落混排。
### 3.5 解读与落事
「解」是结果页唯一主操作。点击后先选择或显示当前方式:
- “本地解读”:立即、离线,不发送问题;
- “AI 解读”:说明将发送问题、本卦、之卦和动爻文本,首次需要明确同意。
AI 加载超过 300ms 时显示内联进度;按钮在请求期间禁用,避免重复提交。超时后显示“保留当前结果,可重试或改用本地解读”,不能把结果页替换为空白错误页。
结尾固定使用“可以试的一小步”区域,包含时间范围和撤销方式;它是建议,不是判词。
## 4. 视觉系统
### 4.1 风格关键词
内容优先、纸张感、墨色、高留白、细线、克制朱砂、现代中文排版。
避免:龙凤、满屏祥云、旋转太极、金色发光、仿古卷轴、低对比度水墨、正文毛笔字、伪造印章和无意义繁体字。
### 4.2 颜色令牌
以下是起始令牌,不允许在页面中散落硬编码颜色:
| 语义 | 浅色主题 | 用途 |
|---|---|---|
| `background` | `#F7F2E8` | 暖纸背景 |
| `surface` | `#FFFDF7` | 正文和卡片表面 |
| `onBackground` | `#1F1B16` | 墨色主文字 |
| `onSurfaceVariant` | `#655E55` | 次级说明 |
| `primary` | `#8C2F2B` | 朱砂主操作、动爻强调 |
| `onPrimary` | `#FFFFFF` | 主操作文字 |
| `secondary` | `#765D3E` | 旧铜色次级强调 |
| `outline` | `#B9AEA0` | 分隔线和输入边界 |
| `error` | Material 语义错误色 | 错误,不与朱砂强调混用 |
深色主题不是简单反色。建议使用近墨背景、暖白正文和降低饱和度的朱砂色,并独立验证所有对比度。最终令牌以无障碍测试结果为准。
### 4.3 字体与排版
- 标题、卦名、短卦辞可使用授权清晰的思源宋体/Noto Serif CJK 子集。
- 按钮、输入、长篇说明和系统信息使用系统中文无衬线或思源黑体。
- 毛笔字体只允许出现在经过审查的品牌字形中,不能用于正文和关键操作。
- 正文基准不小于 16sp,行高约 1.5–1.7;长文在平板上限制行宽。
- 所有字号通过 `MaterialTheme.typography` 语义令牌提供,不在 Composable 中硬编码。
字体资源必须评估 APK 体积、授权和字形覆盖;不能为了视觉一致性下载不明来源字体。
### 4.4 形状与材质
- 使用 4/8dp 间距体系,页面以 24dp 左右的宽松留白为主。
- 卡片圆角克制,避免所有内容都成为悬浮大圆角卡片。
- 阴阳爻线、分隔线和图标使用统一粗细。
- 纸纹若使用,透明度必须极低、可移除,且不得影响滚动性能与文字对比度。
- 图标使用同一套矢量图标;Emoji 不作为结构性图标。
## 5. 动画与触觉
动画必须表达因果:确认一轮后,对应爻线从下向上出现;从本卦到之卦时,仅动爻发生形态过渡。
- 触控反馈在 100ms 内出现。
- 微动画通常为 150–300ms,复杂过渡不超过 400ms。
- 一屏同时动画的重点元素不超过两个。
- 动画可中断,不阻止返回和触控。
- 系统减少动态效果时关闭非必要过渡。
- 触觉只用于确认单爻、完成六爻和显式错误,不对滚动或普通切换连续震动。
## 6. 无障碍验收
- 所有交互目标至少 48×48dp,邻近目标间距至少 8dp。
- 正文与背景对比度至少 4.5:1;大图形和非文字元素至少 3:1。
- 动爻同时使用颜色、形状/符号和文字说明。
- 读屏顺序与视觉顺序一致;卦象有完整语义描述,装饰纸纹不进入语义树。
- 最大系统字体下无关键文字截断,主按钮仍可见或可滚动到达。
- 横屏、小屏和大屏不产生水平滚动;长文在平板上限制阅读宽度。
- 错误信息靠近问题控件,包含原因和恢复动作。
## 7. 文案规范
推荐语气:平实、留有余地、说明来源。
| 避免 | 推荐 |
|---|---|
| “此卦预示你一定会成功” | “这段材料提醒你留意……” |
| “现在绝不能辞职” | “在不可逆决定前,可以先验证一个较小假设” |
| “AI 大师解卦” | “AI 解读” |
| “再算一次改变运势” | “重新开始一段记录” |
| “结果加载中”用于本地计算 | 本地结果即时展示 |
解释中的经典文本、编辑撰写的本地白话、AI 生成内容必须分别标注。
## 8. UI 完成检查表
- [ ] 每屏只有一个主操作。
- [ ] 投币进度和计值约定始终可见。
- [ ] 卦象不是包含文字的位图。
- [ ] 动爻不只靠颜色表达。
- [ ] 本地与 AI 内容来源可辨认。
- [ ] 加载、空、离线、超时和非法响应均有恢复路径。
- [ ] 浅色、深色、最大字体、读屏、减少动态效果均验证。
- [ ] 无龙凤祥云等与功能无关的“古风贴图”。
本规范采用内容优先、纸张阅读、低动态和宽松密度的移动端设计原则,并结合 Android 原生交互要求。工程实现仍应遵循 [Material 3](https://m3.material.io/) 与 [Compose 无障碍](https://developer.android.com/develop/ui/compose/accessibility)的最新官方指南。
+3
View File
@@ -0,0 +1,3 @@
写下一件正在纠结的事,亲手抛三枚铜币,六次成卦,然后看本卦、之卦、卦辞和动爻。点「解」以后,才会用本地内容或者 AI 把这些东西翻译成人话。
AI 不参与起卦。卦是本地生成的,AI 只负责解释已经发生的结果,最后尽量收在一件现实中能做、而且可以反悔的小事上。它不会告诉你什么时候发财,也不会替你决定要不要辞职、分手或者买股票。