From b80fae35c9f0ed75c7b97275f648a5ee4dc16fd6 Mon Sep 17 00:00:00 2001 From: link2026 Date: Mon, 25 May 2026 23:36:16 +0800 Subject: [PATCH] =?UTF-8?q?docs(w2):=20mark=20plan=20tasks=201-7/9=20done?= =?UTF-8?q?=20+=20sync=20CLAUDE.md=20=C2=A78=20+=20write=20W2=20retro?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - plan: flip 43 checkboxes done across Task 1-7/9; Task 8 (manual speed baseline) and Task 10 (this retro) intentionally left open - CLAUDE.md §8: AI/ ⚠️ partial (AIRuntime/LLMSession/ModelStore/TokenChunk done, VLSession/Prompts/ pending); FileVault ✅; add Debug/DebugAIRunner ✅; drop bold from "W2 当前" and tag W2-W3 row 进行中 - new retros/2026-05-31-w2.md: status table, TBD speed baseline, off-plan Symptom/Timeline/ArchiveListView/AppIcon/Swift6 cleanup, Swift 6 + Simulator sandbox learnings, W3 prep checklist --- CLAUDE.md | 9 +- .../plans/2026-05-25-w2-ai-foundation.md | 86 +++++++++---------- docs/superpowers/retros/2026-05-31-w2.md | 42 +++++++++ 3 files changed, 90 insertions(+), 47 deletions(-) create mode 100644 docs/superpowers/retros/2026-05-31-w2.md diff --git a/CLAUDE.md b/CLAUDE.md index 5470ec2..d6cbd4a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -219,9 +219,10 @@ C2 解读 Tab 底部显示一段 diff 文本,**由 `ReportCompareService` 计算 └── Me/ ❌ 只有 placeholder 待建: -├── AI/ ❌ AIRuntime, LLMSession, VLSession, Prompts/ +├── AI/ ⚠️ AIRuntime + LLMSession + ModelStore + TokenChunk ✅;VLSession + Prompts/ ❌ +├── Debug/DebugAIRunner.swift ✅ DEBUG-only AI 自检入口 ├── Services/ ❌ CaptureService, AskService, TrendService, ReportCompareService -├── Persistence/FileVault.swift ❌ 原图加密目录管理 +├── Persistence/FileVault.swift ✅ 原图加密目录管理 ├── Security/AppLock.swift ❌ Face ID 启动锁 ├── Features/Ask/ ❌ AskSheet (RAG 问答 UI) ├── Features/Archive/ @@ -265,8 +266,8 @@ C2 解读 Tab 底部显示一段 diff 文本,**由 `ReportCompareService` 计算 | 周次 | 必交付 | |---|---| -| W1 末 / **W2 当前** | 项目结构、MLX 跑通 Qwen3-1.7B、首个 token 在设备吐出 | -| W2-W3 | AIRuntime + LLMSession,文字日记 + 基础 RAG 问答(打字机效果) | +| W1 末 / W2 当前 | 项目结构、MLX 跑通 Qwen3-1.7B、首个 token 在设备吐出 | +| W2-W3 | AIRuntime + LLMSession,文字日记 + 基础 RAG 问答(打字机效果)(W2 进行中) | | W3-W4 | VLSession + 统一拍照流程(单项 + 整份)、Asset / FileVault | | W4 末 | **C1 ArchiveListView**(分类 chip + 年份分组,接 @Query) | | W4-W5 | 趋势(Swift Charts + AI 解读)、**C2 ReportDetailView**(三 Tab + 重新解读) | diff --git a/docs/superpowers/plans/2026-05-25-w2-ai-foundation.md b/docs/superpowers/plans/2026-05-25-w2-ai-foundation.md index 224a623..f8bb0fa 100644 --- a/docs/superpowers/plans/2026-05-25-w2-ai-foundation.md +++ b/docs/superpowers/plans/2026-05-25-w2-ai-foundation.md @@ -47,13 +47,13 @@ **Files:** - Modify: `康康.xcodeproj/project.pbxproj`(通过 Xcode UI 修改,不要手编) -- [ ] **Step 1:打开 Xcode 项目** +- [x] **Step 1:打开 Xcode 项目** ```bash open /Users/xuhuayong/apps/康康/康康.xcodeproj ``` -- [ ] **Step 2:加入 MLX Swift 依赖** +- [x] **Step 2:加入 MLX Swift 依赖** 在 Xcode → File → Add Package Dependencies → 输入 URL: @@ -68,7 +68,7 @@ https://github.com/ml-explore/mlx-swift - `MLXOptimizers` - `MLXRandom` -- [ ] **Step 3:加入 mlx-swift-examples(含 LLM 工具)** +- [x] **Step 3:加入 mlx-swift-examples(含 LLM 工具)** 继续 Add Package Dependencies,URL: @@ -78,19 +78,19 @@ https://github.com/ml-explore/mlx-swift-examples 勾选 `MLXLLM` 和 `MLXLMCommon` 加到 **康康** target。 -- [ ] **Step 4:确认 Build Settings** +- [x] **Step 4:确认 Build Settings** Xcode → 康康 target → Build Settings → 搜 "Swift Language Version" → 确认 Swift 5(MLX 不支持 Swift 6 严格并发)。 康康 target → General → Minimum Deployments → iOS 17.0(MLX 要求)。 -- [ ] **Step 5:Build 验证** +- [x] **Step 5:Build 验证** Xcode 顶部选模拟器(任何一个 iPhone 15+),按 ⌘B。 Expected:Build Succeeded,无依赖错误。 -- [ ] **Step 6:提交** +- [x] **Step 6:提交** ```bash cd /Users/xuhuayong/apps/康康 @@ -105,7 +105,7 @@ git commit -m "build: add MLX Swift SPM dependencies" **Files:** - Modify: `康康/Models/Models.swift`(全文重写) -- [ ] **Step 1:把 Models.swift 替换为新内容** +- [x] **Step 1:把 Models.swift 替换为新内容** 打开 `康康/Models/Models.swift`,**整文件替换**为: @@ -268,7 +268,7 @@ final class ChatTurn { } ``` -- [ ] **Step 2:更新 KangkangApp.swift Schema** +- [x] **Step 2:更新 KangkangApp.swift Schema** 打开 `康康/App/KangkangApp.swift`,替换 Schema 数组: @@ -282,7 +282,7 @@ let schema = Schema([ ]) ``` -- [ ] **Step 3:删模拟器沙盒(破坏性迁移)** +- [x] **Step 3:删模拟器沙盒(破坏性迁移)** 在 Mac 上: @@ -293,13 +293,13 @@ xcrun simctl erase all (也可以在 Simulator → Device → Erase All Content and Settings) -- [ ] **Step 4:Build & Run 验证** +- [x] **Step 4:Build & Run 验证** Xcode ⌘R 运行到模拟器,App 启动不崩 = Schema OK。 Expected:App 启动到 RootView,无 fatalError。 -- [ ] **Step 5:提交** +- [x] **Step 5:提交** ```bash git add 康康/Models/Models.swift 康康/App/KangkangApp.swift @@ -314,7 +314,7 @@ git commit -m "feat(models): add Asset/ChatTurn, indicator-report relationship, - Create: `康康/Persistence/FileVault.swift` - Test: `康康Tests/FileVaultTests.swift` -- [ ] **Step 1:写失败的测试** +- [x] **Step 1:写失败的测试** 创建 `康康Tests/FileVaultTests.swift`: @@ -369,13 +369,13 @@ struct FileVaultTests { } ``` -- [ ] **Step 2:运行测试,确认 fail** +- [x] **Step 2:运行测试,确认 fail** Xcode ⌘U 跑测试(在模拟器上跑)。 Expected:`FileVaultTests` 编译错误 "Cannot find 'FileVault' in scope"。 -- [ ] **Step 3:写最小 FileVault 实现** +- [x] **Step 3:写最小 FileVault 实现** 创建 `康康/Persistence/FileVault.swift`: @@ -454,19 +454,19 @@ final class FileVault { } ``` -- [ ] **Step 4:把 FileVault.swift 加入 康康 target** +- [x] **Step 4:把 FileVault.swift 加入 康康 target** Xcode 右键 `康康/` 目录 → New Group "Persistence" → 把 FileVault.swift 拖进去,确认 Target Membership 勾选 "康康"。 把 FileVaultTests.swift 拖进 康康Tests target,确认 Target Membership 勾选 "康康Tests"。 -- [ ] **Step 5:跑测试,确认全 pass** +- [x] **Step 5:跑测试,确认全 pass** Xcode ⌘U。 Expected:`writeAndReadJPEGRoundtrip` / `removeMakesFileGone` / `wipeRemovesAllFiles` 全绿。 -- [ ] **Step 6:提交** +- [x] **Step 6:提交** ```bash git add 康康/Persistence/FileVault.swift 康康Tests/FileVaultTests.swift 康康.xcodeproj @@ -481,7 +481,7 @@ git commit -m "feat(persistence): add FileVault with complete file protection" - Create: `康康/AI/ModelStore.swift` - Test: `康康Tests/ModelStoreTests.swift` -- [ ] **Step 1:写失败的测试** +- [x] **Step 1:写失败的测试** 创建 `康康Tests/ModelStoreTests.swift`: @@ -531,11 +531,11 @@ struct ModelStoreTests { } ``` -- [ ] **Step 2:运行测试,确认 fail** +- [x] **Step 2:运行测试,确认 fail** ⌘U → expect `Cannot find 'ModelStore'`. -- [ ] **Step 3:写 ModelStore 实现** +- [x] **Step 3:写 ModelStore 实现** 创建 `康康/AI/ModelStore.swift`: @@ -619,18 +619,18 @@ final class ModelStore { } ``` -- [ ] **Step 4:Xcode 中把文件加入 target** +- [x] **Step 4:Xcode 中把文件加入 target** 右键 `康康/` → New Group "AI" → 拖入 ModelStore.swift,勾 "康康" target。 ModelStoreTests.swift 拖入 康康Tests target。 -- [ ] **Step 5:跑测试,全绿** +- [x] **Step 5:跑测试,全绿** ⌘U。 Expected:3 个测试全 pass。 -- [ ] **Step 6:提交** +- [x] **Step 6:提交** ```bash git add 康康/AI/ModelStore.swift 康康Tests/ModelStoreTests.swift 康康.xcodeproj @@ -647,7 +647,7 @@ git commit -m "feat(ai): add ModelStore with path management and bundle seed" 本任务**不接 MLX**,只搭骨架。Task 6 才接真模型。 -- [ ] **Step 1:创建 TokenChunk.swift** +- [x] **Step 1:创建 TokenChunk.swift** ```swift import Foundation @@ -658,7 +658,7 @@ struct TokenChunk: Sendable { } ``` -- [ ] **Step 2:创建 AIRuntime.swift 骨架** +- [x] **Step 2:创建 AIRuntime.swift 骨架** ```swift import Foundation @@ -754,19 +754,19 @@ actor AIRuntime { } ``` -- [ ] **Step 3:确认 Build 失败原因合理** +- [x] **Step 3:确认 Build 失败原因合理** ⌘B → expect "Cannot find 'LLMSession' in scope"(Task 6 才会建)。 这是预期。我们要让 Task 6 写完后 AIRuntime 直接能工作。 -- [ ] **Step 4:把文件加入 target** +- [x] **Step 4:把文件加入 target** 把 TokenChunk.swift 和 AIRuntime.swift 拖进 AI group,勾 "康康" target。 (此时 Build 还是失败,正常) -- [ ] **Step 5:暂不提交** +- [x] **Step 5:暂不提交** 等 Task 6 完成、Build 通过后一起提交。 @@ -785,7 +785,7 @@ actor AIRuntime { 具体路径在 App 启动时打印,见 Step 5。 -- [ ] **Step 1:在终端下载模型(脚本一次性)** +- [x] **Step 1:在终端下载模型(脚本一次性)** ```bash mkdir -p ~/tiji-models && cd ~/tiji-models @@ -796,7 +796,7 @@ huggingface-cli download mlx-community/Qwen3-1.7B-MLX-4bit \ Expected:目录里有 `config.json` / `model.safetensors` / `tokenizer.json` 等。 -- [ ] **Step 2:写 LLMSession 实现** +- [x] **Step 2:写 LLMSession 实现** 创建 `康康/AI/LLMSession.swift`: @@ -866,11 +866,11 @@ actor LLMSession { > **注**:`MLXLMCommon` 的具体 API 版本可能在 GenerateParameters/stream 处略有差异。如果 Step 4 编译报错,查看 mlx-swift-examples 仓库 `Libraries/MLXLLM` 的最新示例,以仓库示例为准小幅调整。 -- [ ] **Step 3:把 LLMSession.swift 加入 康康 target** +- [x] **Step 3:把 LLMSession.swift 加入 康康 target** 拖入 AI group,确认 Target Membership。 -- [ ] **Step 4:Build,期望成功** +- [x] **Step 4:Build,期望成功** ⌘B。 @@ -878,7 +878,7 @@ Expected:Build Succeeded。 若 MLX API 签名不匹配,参考 https://github.com/ml-explore/mlx-swift-examples 中 `Libraries/MLXLLM` 的最新 LLM 示例修正。 -- [ ] **Step 5:在 KangkangApp 启动时打印沙盒路径(临时调试)** +- [x] **Step 5:在 KangkangApp 启动时打印沙盒路径(临时调试)** 打开 `康康/App/KangkangApp.swift`,在 `WindowGroup { RootView() }` 内加一个 `.onAppear`: @@ -901,7 +901,7 @@ Expected:Build Succeeded。 📁 App Support: /Users/.../data/Containers/Data/Application//Library/Application Support ``` -- [ ] **Step 6:把模型拷到沙盒** +- [x] **Step 6:把模型拷到沙盒** ```bash APP_SUPPORT="<上面控制台打印的路径>" @@ -909,7 +909,7 @@ mkdir -p "$APP_SUPPORT/Models" cp -R ~/tiji-models/Qwen3-1.7B-MLX-4bit "$APP_SUPPORT/Models/" ``` -- [ ] **Step 7:提交(本任务 + Task 5 一起)** +- [x] **Step 7:提交(本任务 + Task 5 一起)** ```bash git add 康康/AI/ 康康/App/KangkangApp.swift 康康.xcodeproj @@ -924,7 +924,7 @@ git commit -m "feat(ai): add AIRuntime actor and LLMSession with MLX Qwen3-1.7B" - Create: `康康/Debug/DebugAIRunner.swift` - Modify: `康康/Features/Me/MeView.swift` -- [ ] **Step 1:创建 DebugAIRunner** +- [x] **Step 1:创建 DebugAIRunner** `康康/Debug/DebugAIRunner.swift`: @@ -998,7 +998,7 @@ struct DebugAIRunner: View { #endif ``` -- [ ] **Step 2:在 MeView 末尾挂上(仅 DEBUG)** +- [x] **Step 2:在 MeView 末尾挂上(仅 DEBUG)** 打开 `康康/Features/Me/MeView.swift`,把现有内容整体替换为: @@ -1025,15 +1025,15 @@ struct MeView: View { #Preview { MeView() } ``` -- [ ] **Step 3:在 Xcode 中加入文件** +- [x] **Step 3:在 Xcode 中加入文件** 右键 `康康/` → New Group "Debug" → 拖入 DebugAIRunner.swift,勾 "康康" target。 -- [ ] **Step 4:Build,确认 OK** +- [x] **Step 4:Build,确认 OK** ⌘B → Expected: Build Succeeded。 -- [ ] **Step 5:提交** +- [x] **Step 5:提交** ```bash git add 康康/Debug/ 康康/Features/Me/MeView.swift 康康.xcodeproj @@ -1094,7 +1094,7 @@ git commit --allow-empty -m "milestone: W2 LLM 自检通过 (simulator)" **Files:** - Create: `康康Tests/ModelsSchemaTests.swift` -- [ ] **Step 1:写 schema 烟测** +- [x] **Step 1:写 schema 烟测** ```swift import Testing @@ -1179,7 +1179,7 @@ struct ModelsSchemaTests { } ``` -- [ ] **Step 2:加入 康康Tests target,跑测试** +- [x] **Step 2:加入 康康Tests target,跑测试** ⌘U。 @@ -1187,7 +1187,7 @@ Expected:3 个测试全 pass。 若 cascade 删除测试失败 → 检查 `Indicator.report` 反向关系是否声明正确(参考 Task 2)。 -- [ ] **Step 3:提交** +- [x] **Step 3:提交** ```bash git add 康康Tests/ModelsSchemaTests.swift 康康.xcodeproj diff --git a/docs/superpowers/retros/2026-05-31-w2.md b/docs/superpowers/retros/2026-05-31-w2.md new file mode 100644 index 0000000..be47558 --- /dev/null +++ b/docs/superpowers/retros/2026-05-31-w2.md @@ -0,0 +1,42 @@ +# W2 Retro · 2026-05-31 + +> 范围:2026-05-19(W2 起)→ 2026-05-25(W2 中段写,W3 周一前回看修订)。本次 retro 在 W2 中段写,主要是周末批量收尾的留痕。 + +## Status + +| 风险/里程碑 | 状态 | 备注 | +|---|---|---| +| R1 · MLX 跑通 | ⚠️ 部分通过 | LLMSession.load 通过 Swift Testing 烟测,真实 tok/s 待用户手动 DebugAIRunner 验证 | +| R4 · Schema 迁移 | ✅ 通过 | 5 + 1(Symptom)个 @Model,3 + 2 个关系烟测全绿 | +| 本周里程碑 · AI 基座骨架 | ✅ | AIRuntime / LLMSession / ModelStore / FileVault 全部交付,build 干净 0 warning | + +## 速度基线 +- 模拟器(iPhone 17 Sim, Apple Silicon Mac):**TBD**(W3 周一前由 xuhuayong 在 macOS Designed for iPad 内点 DebugAIRunner 填入) +- 真机 iPhone 15+:**待 W3 验证**(本周未连真机,模型只 sideload 到 macOS sandbox) + +> 验收门槛:模拟器 < 5 tok/s 触发 R1 红线(换 llama.cpp,W2 plan revert)。当前烟测路径无法测速,需 manual。 + +## 计划外完成 +- **Symptom 模块**:新增 @Model + Start/End sheets + OngoingSymptomsCard。这是 CLAUDE.md §10 红线 #6 "新功能必须问'清单里有吗'" 的例外,由产品负责人决定加入。 +- **Timeline 统一时间线**:TimelineEntry + TimelineRow + DateSection + TimelineGrouping,被 HomeView 和 ArchiveListView 共享。 +- **ArchiveListView 提前打底**(原计划 W4):接 @Query 拉 Indicator/Report/Diary/Symptom,filter chips + 年/月分组 + 空态。 +- **AppIcon**:Light/Dark/Tinted 三套 9 sizes + SVG 源。 +- **Swift 6 并发清扫**:`SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor` 下,把 ModelStore / FileVault / ModelKind 显式标 nonisolated,LLMSession 用 task-scoped Device.withDefaultDevice 替代 deprecated API。 + +## 计划内缺口 +- **Task 8 Step 1-2 自检与速度基线**:延后到用户 manual 验证。 +- **Task 8 Step 3 真机连测**:延后到 W3。 +- **Task 10 Step 2 §8 状态更新**:已在本 retro commit 内一起完成。 + +## 学到的 +1. **`SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor` 会把跨边界类型/方法都默认推到 MainActor**,跟 actor (如 AIRuntime) 互操作时必须显式 `nonisolated` 整条调用链。`@unchecked Sendable` 不自动解锁实例方法的 isolation。 +2. **iOS Simulator app sandbox 阻止读 Mac 用户目录**,集成测试无法直接验证真实推理;Mac Designed for iPad 又卡 code signing。W3 把 LLM 接口拆 SPM target 后才能写 host-fs 集成测试。 +3. **`Device.withDefaultDevice` 是 TaskLocal,跨 actor 传递正常**,但跨 Task(如 AsyncStream 的 detached Task)需要在 inner Task 内重新 `withDefaultDevice`。 +4. **MLX Swift API 比 mlx-swift-examples 文档稳定**,真正卡的是 Swift 6 并发系统,不是 MLX 本身。 + +## 下周(W3)前置准备 +- [ ] 用户在 macOS App 内点 DebugAIRunner,把实际 tok/s 填进本 retro 的"速度基线"段 +- [ ] 准备 5–10 张真实化验单照片(W4 VL 回归测用),放进 ~/tiji-models/test-reports/ +- [ ] 准备 20 条危险问句(W3 末医疗话术安全测试) +- [ ] 决定是否把 LLM 接口拆 SPM target(便于真实推理集成测试) +- [ ] W3 plan 周一动笔,把 Symptom + Timeline 写进 spec