# Spec Kit Overview

本文件用于简短记录当前项目每个 spec 的核心内容摘要，便于快速总览与追踪变更。

## Client Bootstrap

- **目标**：初始化正念 APP 客户端工程规范，确保可运行、可切环境、可多语言扩展、目录标准化、可 EAS 打包
- **核心范围**：客户端工程骨架与文档（`client/README.md`）、环境变量约定、i18n（CN/EN/ES/PT/TC）、推送/小组件/卡片滑动的一期能力约束
- **主要约定**：
  - 环境：`.env.dev` / `.env.prod`，推荐 `EXPO_PUBLIC_` 前缀
  - 语言码：`zh-CN/en/es/pt/zh-TW`
  - 目录：`app/`（可选 expo-router）+ `src/`（features/services/store/utils 等）+ `assets/`
  - 打包：EAS Build，dev/prod bundle id 约定
- **阶段产物**：
  - `spec_kit/Client Bootstrap/spec.md`
  - `spec_kit/Client Bootstrap/plan.md`
  - `spec_kit/Client Bootstrap/tasks.md`
- **已完成编码（阶段性）**：
  - Expo 工程已在 `client/` 初始化，并完成 `pnpm install`
  - i18n 基座已接入：5 份语言资源 + 设备语言优先/设置可切换/持久化 + 入口初始化
  - 推荐 Feed 请求链路增加调试日志（仅开发环境）：打印 Feed API 请求头与请求体，便于联调排查
  - 环境变量注入增加调试日志（仅开发环境）：打印参与 `API_BASE_URL` 计算的 `EXPO_PUBLIC_*` 与最终解析结果，便于定位“打到哪个后端”

## Client User Identity

- **目标**：在无账号体系或账号可选的前提下，为客户端建立稳定的 `client_user_id`，用于与 APNs/FCM 的 Push Token 做绑定，支撑精准推送与 Token 变更更新
- **关键结论**：`client_user_id` 使用**随机 UUID（默认 UUID v4）**合适；将其视为不透明字符串，不使用可追踪设备硬件标识
- **阶段产物**：
  - `spec_kit/Client User Identity/spec.md`

## App Push

- **目标**：实现“每日提醒”推送闭环：首次进入 Onboarding 可选择每日推送次数（0～5，可跳过），个人主页每日提醒弹窗可改次数/关闭；客户端使用首次生成 UUID（`client_user_id`）与后端关联；后端按用户配置定时推送
- **关键选型**：客户端继续使用 `expo-notifications`；后端使用 Expo Push Service + 定时任务（Celery/等价方案）按用户偏好发送
- **阶段产物**：
  - `spec_kit/App Push/spec.md`
  - `spec_kit/App Push/plan.md`
  - `spec_kit/App Push/tasks.md`
- **已完成编码（阶段性）**：
  - 客户端：新增 `client_user_id`（UUID v4）生成与持久化；每日提醒次数范围修正为 **0～5**（0 表示关闭）
  - 客户端：Onboarding 结束页（每日提醒）在用户选择次数 > 0 时**直接触发系统权限申请**；授权后获取 Expo Push Token 并调用后端 `register/preferences`（移除单独的 push 引导页）
  - 客户端：个人主页“每日提醒”弹窗移除测试模式强制无权限逻辑，改为真实读取系统权限；并在开关/点击 OK 时同步后端偏好
  - 客户端：新增推送接口封装 `client/src/services/pushApi.ts`（token 获取、register/preferences/get、自动上报时区与 locale，并携带用户画像供后端 Push 模板使用）
  - 后端：新增 Push 数据模型 + Alembic 迁移（`push_tokens` / `push_preferences` / `push_send_log`）
  - 后端：新增 Push API（`/v1/push/register`、`/v1/push/preferences`、`/v1/push/test`（dev）），并新增基础限流
  - 后端：实现 Celery 定时任务
    - 每日生成计划（按用户时区在 9:00～24:00 均匀分段随机抖动生成 0～5 个时间点）
    - ETA 发送任务（幂等：同一用户同一天同一 slot 只发一次；用户中途关闭/降次数会跳过）
    - 发送文案复用推荐模块 `scene="push"`（降风险）
  - 后端：`pytest` 全量通过（27 passed）

## Project Bootstrap

- **目标**：完成项目仓库初始化与工程约定落地，明确 client/server 结构、dev/pro 环境隔离、MySQL/Redis 资源命名与访问边界
- **核心范围**：根目录结构与文档、dev/pro 配置策略、MySQL schema 命名（`mindfulness_dev`/`mindfulness`）、Redis ACL + key 前缀隔离（`dev:*`/`pro:*`）
- **主要约定**：
  - MySQL：prod=`mindfulness`，dev=`mindfulness_dev`，统一 `utf8mb4` 与 snake_case
  - Redis：单实例，通过 ACL 限制不同用户只能访问对应前缀；应用侧强制 key 使用 `dev:`/`pro:` 前缀
  - 安全：真实 IP/账号/密码/Token 不写入仓库，仅提供 `.env.example` 结构
- **阶段产物**：
  - `spec_kit/Project Bootstrap/spec.md`
- **近期变更**：
  - 新增后端镜像构建基础：`server/Dockerfile`、`server/.dockerignore`
  - 新增后端镜像打包与推送工作流：`.gitea/workflows/server-build.yml`（自动递增 semver tag 并推送到 Docker Hub）
  - 新增后端蓝绿部署工作流：`.gitea/workflows/server-deploy.yml`（SSH 上机 + Docker 蓝绿启动 + Nginx upstream 切流 + 健康检查与失败回滚）
  - 后端配置缺失时报错增强：`app/core/config.py` 将缺失的必填环境变量用中文提示并给出注入方式；补充 `server/env.example` 与 Docker 运行说明

## Onboarding App Shell

- **目标**：落地首次进入体验（Onboarding 3–5 页可跳过 + 每日提醒可跳过/可关闭）与主应用壳（点赞/讨厌、收藏夹入口、通用设置入口）
- **核心范围**：Onboarding 多页流程、主界面反应操作（Like/Dislike）、收藏夹、设置页（版本、iOS 小组件入口/说明）
- **阶段产物**：
  - `spec_kit/Onboarding App Shell/spec.md`
  - `spec_kit/Onboarding App Shell/plan.md`
  - `spec_kit/Onboarding App Shell/tasks.md`

## iOS Widget

- **目标**：实现真正的 iOS 桌面小组件（WidgetKit Extension），展示美观文案并与 App 共享数据
- **核心范围**：WidgetKit 扩展、App Group 数据共享、尺寸适配（Small/Medium/Large）、点击跳转回 App
- **阶段产物**：
  - `spec_kit/iOS Widget/spec.md`
  - `spec_kit/iOS Widget/plan.md`
  - `spec_kit/iOS Widget/tasks.md`
- **近期变更**：
  - iOS 主 App Bundle ID 已统一为 `com.damer.mindfulness`，Widget Extension 为 `com.damer.mindfulness.emotionwidget`
  - 修复 iOS 签名 Team 配置不一致：为主 App（Debug）与 Widget Extension（Debug/Release）显式补齐 `DEVELOPMENT_TEAM=WS92GPX9H2`，避免打包/上传过程中回退到默认 Team 导致显示 “Other Team”
  - iOS 构建号已提升到 `2`，并将 `client/ios/client/Info.plist` 改为自动跟随 `MARKETING_VERSION` / `CURRENT_PROJECT_VERSION`
  - 推送 entitlements 的 `aps-environment` 已切到 `production`（用于 TestFlight/线上包）
  - 清理未接入编译的 WidgetKit 骨架残留：移除磁盘上的 `client/ios/MindfulnessWidget/` 文件，并从 `client/ios/client.xcodeproj/project.pbxproj` 删除对应工程引用（避免 Xcode 显示幽灵文件）
  - 修复 Xcode Archive 偶发显示 “Generic Xcode Archive”：在共享 scheme `Hey Mama` 的 Archive Post-actions 自动补齐 `.xcarchive/Info.plist` 的 `ApplicationProperties`，并在缺失时补齐 `Name`/`SchemeName` + 自检提示（根治 Organizer 无法识别主 App、无法分发/上传 TestFlight 的问题）

## Splash Consent

- **目标**：实现开屏页（使用 `client/assets/images/index/` 图片资源），首次加载展示渐变同意按钮并提供隐私/协议入口
- **核心范围**：开屏 UI、首次同意状态持久化（`consent.accepted`）、点击同意后进入后续 Onboarding/App 流程
- **阶段产物**：
  - `spec_kit/Splash Consent/spec.md`
  - `spec_kit/Splash Consent/plan.md`
  - `spec_kit/Splash Consent/tasks.md`
- **近期变更**：
  - 启动流程优化：将 Expo Router 初始路由调整为协议页 `/(splash)/splash`，并在协议页已同意时直接分发到 `/(app)/home` 或 `/(onboarding)/onboarding`，避免系统开屏结束后先渲染 `index`（转圈页）再跳协议页导致的“闪一下”

## Policy Links

- **目标**：在客户端首次进入同意页底部补充「隐私协议/用户使用协议」入口，并将协议链接改为**由后端托管与按设备语言下发**（默认回退 EN）
- **核心范围**：后端提供协议链接下发接口（可配置、可多语言）；客户端按设备语言请求并展示链接，失败不阻塞“同意并继续”
- **阶段产物**：
  - `spec_kit/Policy Links/spec.md`
  - `spec_kit/Policy Links/plan.md`
  - `spec_kit/Policy Links/tasks.md`
- **已完成编码（任务清单已执行完毕）**：
  - 后端新增 `GET /v1/legal/links`（按 `Accept-Language` 分发，默认回退 EN，返回 `resolvedLang`）
  - 后端配置：`LEGAL_*` 环境变量（`server/env.example` 已补齐示例；不配置时走内置协议页兜底）
  - 后端新增内置协议内容页：
    - `GET /v1/legal/privacy`
    - `GET /v1/legal/terms`
  - 客户端新增协议接口封装 `client/src/services/legalApi.ts`
  - 客户端工程化：新增统一 HTTP 封装 `client/src/utils/http.ts`（baseURL/超时/JSON/统一错误），并将 `legalApi.ts` / `recoApi.ts` 接入
  - 客户端接入两处入口：`app/(splash)/splash.tsx`、`components/home/ProfileModal.tsx`
  - **变更文件清单**：
    - `server/app/api/v1/legal.py`
    - `server/app/core/config.py`
    - `server/app/legal_docs.py`
    - `server/app/main.py`
    - `server/env.example`
    - `server/tests/test_legal_links_unittest.py`
    - `client/src/services/legalApi.ts`
    - `client/src/services/__tests__/legalApi.test.ts`
    - `client/app/(splash)/splash.tsx`
    - `client/components/home/ProfileModal.tsx`
    - `spec_kit/Policy Links/spec.md`
    - `spec_kit/Policy Links/plan.md`
    - `spec_kit/Policy Links/tasks.md`

## Card UI

- **目标**：优化卡片页 UI 与交互，支持右上角主题切换与个人主页弹窗，并将喜欢/讨厌改为 icon + 动效
- **核心范围**：
  - 系统导航栏右上角两个圆形 icon：主题（`theme.svg`）/ 我的（`my.svg`）
  - 底部上拉弹窗（Sheet）：统一内容区背景 `#FAF3EC`，点 X 关闭
  - 主题两种模式：`scenery` / `color`（本期最少影响 Home 页面背景），并持久化 `ui.theme.mode`
  - 喜欢/讨厌按钮：使用 `like.svg` / `hate.svg`，喜欢提供“心形填满”强反馈动效（`like_filled.svg`）
  - 每日提醒弹窗：次数 +/-、Push 提醒开关、渐变 Ok 按钮，并持久化设置
- **阶段产物**：
  - `spec_kit/Card UI/spec.md`
  - `spec_kit/Card UI/plan.md`
  - `spec_kit/Card UI/tasks.md`
- **已完成编码（阶段性）**：
  - 已接入 `react-native-svg-transformer` + `client/metro.config.js`
  - 新增 `SheetModal`、`ThemeModal`、`ProfileModal` 并在 Home 中接入
  - 新增 `DailyReminderModal` 并从个人主页弹窗打开
  - Home：右上角 icon 按钮、主题切换背景、喜欢/讨厌 icon + 动效
  - **修复**：收藏（“我的喜欢”）不展示后端推荐文案的问题——收藏时持久化写入 `text` 快照，并在 `ProfileModal` 的 Favorites 页面同时兼容 Mock 与推荐缓存内容回填

## User Profile Scoring

- **目标**：将 Onboarding 问卷答案映射为标准化“用户画像 U”，并输出硬规则与置信度元信息，供推荐/Push 等模块统一复用
- **核心范围**：mom_stage（离散）、emotion_score（0–1 连续）、context（离散）、need（离散）四维度；以及 `profile_version/source/generated_at/confidence`（V1.1）
- **主要约定**：
  - 输出结构稳定可版本化，便于规则演进
  - 支持题目可跳过：新增 `profile_answered` 标记各题是否作答；字段缺失时由推荐算法走“通用值 + 不确定性惩罚”
  - mom_stage 跳过按安全策略视为 `unknown`（用于避免冒犯），emotion/context/need 跳过不强行填默认值
  - 置信度 `conf_U` = 时间衰减 × 作答完整度（答得越少越不确定），用于推送场景降个性化/降风险
  - 硬规则优先于任何分数计算：`unknown` 仅在“育儿压力且强个性化（personalization_power=1）”时禁推；`block_health_medical` 全场景硬过滤
- **测试覆盖**：提供“输入题目答案 → 输出用户画像”的回归用例，覆盖字段映射、置信度衰减与硬规则触发（可选但建议）
- **阶段产物**：
  - `spec_kit/User Profile Scoring/spec.md`
- **已完成编码（阶段性）**：
  - 客户端 Onboarding 完成时已收集问卷答案并生成用户画像，写入本地存储供推荐/Push/Widget 复用

## SuixinTheme

- **目标**：在 Home 现有「风景 / 纯色」主题基础上新增「随心」主题，背景颜色根据用户问卷画像个性化推荐
- **核心范围**：复用既有 Base Theme（5 套）+ Neutral（1 套），按 `need/stage/emotion/confidence` 选色并做 session 锁定（Theme Lock），支持 TC/EN 文案
- **阶段产物**：
  - `spec_kit/SuixinTheme/spec.md`
  - `spec_kit/SuixinTheme/plan.md`
  - `spec_kit/SuixinTheme/tasks.md`
- **已完成编码（全部）**：
  - 客户端新增第三主题 `suixin`（随心/Ease），与现有主题切换入口一致
  - Home：冷启动会话内锁定 Base Theme；切换文案时在同主题内线性插值输出纯色背景，并持久化 `ui.theme.suixin.state`
  - i18n：新增 `theme.suixin`（TC/EN）
  - 测试：新增随心模块单测（Vitest）并通过；`tsc --noEmit` 通过
- **变更文件**：
  - `client/src/storage/appStorage.ts`
  - `client/app/(app)/home.tsx`
  - `client/components/home/ThemeModal.tsx`
  - `client/src/i18n/locales/all.json`
  - `client/src/utils/bootSession.ts`
  - `client/src/features/suixinTheme/palette.ts`
  - `client/src/features/suixinTheme/colorMath.ts`
  - `client/src/features/suixinTheme/progress.ts`
  - `client/src/features/suixinTheme/pickTheme.ts`
  - `client/src/features/suixinTheme/index.ts`
  - `client/src/features/suixinTheme/__tests__/suixinTheme.test.ts`
  - `client/app/_layout.tsx`
