# J16 · 原型 vs task #178 已交付语义 —— 差异清单

依据：`docs/42-x-day-wellness-plan.md`（Web v1 实现合同）与 `docs/06-user-journey.md#j16`。
对照对象：设计原型 etag `1788857149116559`。

**这一份只列差异，不改语义**（按 @打包的 的要求）。范围限普通用户主链：
创建 / 执行 / 调整 / 暂停恢复 / 复盘。future 能力不展开。

判定用探针跑在导出的 DCLogic 上，不是看图。

---

## 一致的部分（无需改动）

| 合同条款 | 原型实测 |
|---|---|
| 计划 3–30 天 | 2 天和 31 天报错、3 天和 30 天通过 |
| 同时只有一个 active 或 paused 计划 | 已有计划时再创建，计划 id 与天数都不变（新建被拒） |
| 创建前预览每一个有日期的行动 | 向导「See every day」逐日预览，确认前不落库 |
| 每天一条具体、带理由的行动 | 有标题 / 详情 / 理由，取自目录 |
| 依据是创建时快照，之后账号或库存变化不改写计划 | 复盘页写明「Built from your goals on … and the 2 item(s) then in your Fridge」 |
| 完成任务不产生也不修改摄入 | 早前几轮已验证并保持 |
| 今天和过去可完成，过去完成标为补记 | 标「Done · backfilled today」 |
| 未来不可完成 | 对未来日调用完成，状态保持 `todo` |
| 今天和未来可跳过/替换，过去不可新跳过或替换 | 已验证 |
| 完成或跳过可撤销回 planned，计划结束后不可 | `undoAction` 在非 active 时不执行 |
| 暂停不编造结果；恢复只移动暂停日当天及之后仍 planned 的任务，已完成/跳过保持原日 | 恢复预览分「移动」与「保持原日」两栏 |
| 恢复从计划固定时区的次日开始，先给出完整建议日程再确认 | 恢复预览列出全部新日期，确认后才生效 |
| 暂停中的计划不按旧截止日到期（含暂停跨过原截止日） | 已验证 |
| 到期后只读；手动结束是追加 | 到期自动进入 PLAN REVIEW · READ ONLY，无操作按钮 |
| 创建后不能改开始日/天数/时区，只能结束后另建 | 创建后 `planDraft` 为 null，界面无编辑入口 |

---

## 需要你决定的差异

### D1 · 状态词汇：原型 `ended` / 派生 `expired`，合同只有 `completed`

- **合同**：`active`、`paused`、`completed` 是仅有的三个状态；「到期后只读」是 active 的性质，
  不是另一个状态。
- **原型**：结束后 `status = 'ended'`；另有派生的 `expired`（当天超过 `endISO` 且仍 active 时计算得出）。
- **影响**：只是词汇与建模差异，用户可见文案不受影响。但如果设计稿要和实现对齐，
  建议原型改用 `completed`，并把 `expired` 明确表述为「active 且已过截止日 → 只读」的派生视图。
- **要你定**：是否把原型的状态词改成合同口径。

### D2 · 没有修订号与变更身份（revision / clientMutationId / event identity）

- **合同**：每次变更都带 owner 域内的 `clientMutationId`、期望的 revision 和不可变事件身份；
  完全相同的重试返回已存回执；意图不同的重复或过期 revision **fail closed**。
- **原型**：这一层是我前几轮自建的**尝试身份**模型——每次写入尝试有稳定 attempt id，
  模拟存储记录是否被接受，查询按 id 而不是比内容；冲突走「重读 → 保留最新 / 在其之上应用我的」。
  **计划对象上没有 `revision` 字段。**
- **影响**：两者都能表达「重试不重复、冲突要显式选择」，但**不是同一套模型**。
  原型的冲突面板不体现 revision 递增，也不体现「过期 revision 直接失败」。
- **要你定**：设计原型是否需要把 revision 显性化（例如冲突面板显示
  「你基于第 3 版，现在是第 5 版」），还是维持现有的尝试身份表述即可。

### D3 · 逐日任务没有稳定 id，只有数组下标

- **合同**：恢复时移动的任务「保留其 ID 与顺序」，写入端会独立重建目录与允许的状态转移，
  调用方无法伪造任务文案、状态、日期或归属。
- **原型**：`actions[]` 的字段是 `i / dateISO / title / detail / reason / state / backfilled / replaced`
  —— **`i` 是下标，不是稳定 id**。恢复时按下标建映射。
- **影响**：在原型这种单机演示里不会出错；但设计稿如果要表达「任务有身份」，
  目前的模型表达不出来。也意味着原型无法演示「伪造任务被写入端拒绝」这类合同保证。
- **要你定**：是否给逐日任务引入显式 id（纯设计层面即可，不需要真实后端）。

### D4 · 目录未版本化

- **合同**：每日行动「确定性地取自版本化的 `wellness-plan-v1` 目录」。
- **原型**：有目录，但没有版本标识；复盘页也不显示计划是基于哪个目录版本生成的。
- **要你定**：创建快照说明里是否要露出目录版本（例如「catalogue v1」）。

### D5 · 默认时区

- **合同**：所选 IANA 时区不可变，旅行与设备设置不移动计划日期。
- **原型**：向导可选时区（Asia/Tokyo / Europe/London / America/New_York），**默认 Asia/Shanghai**。
- **影响**：行为一致，只是默认值不同；文档示例用的是 America/New_York。
- **要你定**：默认值是否需要统一，或改成跟随设备但创建后锁定。

---

## 我建议的处理顺序

1. **D1 状态词汇** —— 最便宜，纯改名，先做。
2. **D3 任务 id** —— 影响恢复/替换的表达，值得做。
3. **D2 revision 显性化** —— 要先定「设计上要不要让用户看到版本号」，这是产品问题不是实现问题。
4. **D4 / D5** —— 文案与默认值，随下一批一起。

**我没有动任何语义**，等你的判断。
