2026-10-24 CloudKit 生产同步验收
结论:构建 6 已通过 iPhone 与 Mac 的真实 Production 离线新增、修改 / 删除冲突及重启保留复测,已删除正文最终在两端清空。 构建 4 / 5 暴露的正文残留已在修复版验证;首次离线新菜谱缺失仍未复现、根因未知。维护者随后决定剩余验收改用模拟器、不再要求真机,最新覆盖及账号行为边界见模拟器验收记录。本文件保留真实生产测试的范围,不据此扩大为所有账号场景已验证。
验收基线
- 两端:iPhone 15 Pro 与当前 Mac 上的 TestFlight iOS 兼容版;不是模拟器。两端均实际核对为
2.0.0 (4)。 - 分发容器
iCloud.fun.yunle.cook,实际分发包及 Mac 运行包权限为Production,推送为production,get-task-allow = false。 - IPA SHA-256:
c9e91dccf8eba9f3c8572e88cf4090878c641b85c2a1ae444a2174319dd726e5。源码6e4343f的树与合并 #136 的e090866一致;不含后续 #138 的 UI 优化,也不含本次删除修复。 - 五模型生产结构与 Development 的完整回读一致,SHA-256 为
3bb08709e693a1d3b9cafa1b18e86a40f357b88221dd9f93e1bb7347484db271。同一私有数据在两端收敛;没有记录或公开 Apple 账号标识。 - 截图、逐步 UTC 时间、只读存储检查及脱敏运行日志保存在本次任务的
production-sync-acceptance证据目录。所有写操作通过 App 正常界面进行,生产 SQLite 只读检查,不修改数据库。
实际结果
| 场景 | 结果与边界 |
|---|---|
| 私人菜谱新增 | Mac 新建合成菜谱,iPhone 收到名称、鸡蛋及备注 |
| 私人菜谱修改 | iPhone 添加番茄,Mac 已打开的详情自动更新 |
| 私人菜谱在线删除 | iPhone 删除,Mac 列表消失;复测时已打开详情自动变为“菜谱暂不可用”,存储正文清空;重启没有恢复 |
| 冰箱新增 | iPhone 添加胡萝卜,Mac 收到,原有土豆保留 |
| 冰箱修改 | Mac 将到期日改为 2026-11-16,iPhone 已打开列表自动更新为剩余 23 天 |
| 冰箱删除 | Mac 移出测试胡萝卜,iPhone 自动消失;重启后仍只保留原有土豆 |
| 收藏 | Mac 收藏“电饭煲版烧鸡”,iPhone 收到;iPhone 取消后 Mac 收到删除,原有收藏保留 |
| 浏览历史 | Mac 浏览烧鸡,iPhone 最近浏览出现;iPhone 再次打开后,Mac 的浏览时间从 814529379.944477 增至 814529435.185777(Apple epoch),此证据在 Mac 再次进入详情前采集 |
| 偏好 | “不吃辣”分别从 Mac、iPhone 修改,对端当前页面自动更新;结束恢复为关闭,常备油盐、忌口无、提醒关闭均保留 |
| 离线修改 / 在线删除冲突 | 用户确认手机飞行模式且 Wi-Fi 关闭并保存修改;Mac 删除同一测试条目;用户恢复联网后两端都隐藏条目,但只读存储显示 deletedAt 存在、正文重新带回鸡蛋和番茄,长度 396。正文清理失败 |
| 离线新增 | 用户已确认断网保存后在列表看到新条目,但重连后两端均未见。按疑似数据丢失继续调查;缺少重连前的存储快照,未验收通过 |
同步有延迟:收藏取消和反向历史更新约数分钟才收敛,未承诺即时到达。运行日志明确包含 Production 容器及成功导入/导出,也出现 NSCocoaErrorDomain 134417 和 BGSystemTaskSchedulerErrorDomain 3 的瞬态调度错误,随后传输完成。不能据此宣称运行期零错误。控制台当日三条 PUBLIC / WEB / ZoneFetch 事件是控制台访问,不作为 App 私有同步证据。
删除修复与本地验证
生产冲突行保留删除标记,但同步按字段带回了离线正文和较早的修改时间。原实现只在主动删除时清空正文,导入后没有再次清理;仅按更新时间挑选重复行也可能选中晚到的未删除副本。
修复后,同编号只要存在删除标记就不能重新显示或导出;显式恢复仍需使用新编号副本。App 根视图在启动、查询变化和回到前台时,用独立事务清空该编号所有副本的正文,保留删除时间,修正早于删除的更新时间。没有变化时不保存;失败回滚,保留删除标记,在下次导入或前台切换时重试。不会修改其他菜谱或提交其他页面的待保存编辑,不新增 CloudKit 字段。
- 真实合并状态的隔离 SwiftData 重放:修复前断言失败;修复后 11/11 持久化用例通过。此重放只使用合成内存数据,不接入生产库。
- iPhone iOS 27 模拟器:61/61 单元与集成测试通过,包含真实 SwiftUI 根视图观察器在初次显示、晚到导入时自动触发的测试。
- iPad iOS 27 模拟器:62/62 通过(61 项单元与集成测试、1 项私人菜谱新增 / 修改 / 删除界面测试),零失败或跳过。界面测试遇到多次 60 秒的动画等待,MCP 的 300 秒等待超时,但底层进程继续执行;最终以完整 xcresult 和
TEST EXECUTE SUCCEEDED核定通过,保留耗时异常日志。 - Debug 测试构建与 Release 模拟器构建均通过;文档 ESLint 和
git diff --check通过。此前本地回归阶段没有生成或上传新分发包;后续候选准备见下文。
离线新增跟进与构建 5 基线
用户补充确认:原“离线新增验收1024”在断网保存后的列表中出现过。因此不能以操作未完成解释该现象,也不能把删除正文修复当成离线新增丢失的修复。
- USB 只读复制手机的
default.store与 WAL 后,私人菜谱仅有三条已知测试记录,均带删除标记;没有找到独立的新菜谱行。可用持久化历史包含原冲突条目的离线修改和导入删除,没有对应新条目的独立插入。该历史不是完整、不可裁剪的审计日志,不能据此否定用户看到过列表条目。 - 单独离线新增
Offline1024B(鸡蛋):用户确认断网保存后,11:53 UTC 的 USB 只读存储已包含独立新编号和完整正文,11:54 Mac 尚无该编号;用户恢复联网后,11:55 两端存储的编号和正文完全一致,界面也显示该条目。数据库与 WAL 为顺序只读复制,不是原子快照;只保留合成测试记录诊断,完整临时副本检查后清理。 - 跟进时 iPhone 已升级到
2.0.0 (5)。Mac 原位更新遇到 TestFlightASDErrorDomain 663(旧 App 未退出);正常退出旧版后更新成功。现已核对 Mac 实际运行包为构建 5,CloudKit 为Production,推送production,get-task-allow = false。构建 5 含 #138 UI,但仍不含 #142 删除修复;不能替代修复后的生产验收。 - 组合场景复测:先前
102403的保存和修改已在断网前到达 Mac,用户随后确认刚开启飞行模式,因此该轮不计入离线验收。重新用102404(鸡蛋)新增、Offline1024B添加土豆:12:02 UTC 的两端只读检查确认新条目和修改仅在 iPhone,Mac 尚未收到,隔离成立;随后 Mac 删除Offline1024B,正文长度为 0,新条目仍未到达。 - 组合场景重连:12:05 iPhone 已收到删除标记,但旧正文回到 412 字符(鸡蛋、番茄、土豆);新
102404的编号和内容保持完整。12:06 Mac 自动收敛到相同结果。两端列表隐藏被删除条目、显示新条目;iPhone 终止并重新启动后新条目仍在。再次复现删除正文残留,未复现新增丢失。两次受控新增通过不能解释首次缺失,也不代表该首次事件已定位或修复。 - 新增磁盘存储回归:新菜谱保存、关闭并重开存储、另一菜谱接收远端删除并清理、再次重开后,新菜谱内容和编号仍完整保留。这只验证本地持久化与删除清理边界,不模拟真实 CloudKit 传输,也未重现原丢失。
- PR 首次 CI:iPad 69/69 通过;iPhone 68 通过、1 个做菜页面 UI 用例失败。失败现场完整无障碍树及截图均存在“土豆”,但快捷 identifier 查询未找到。改为按明确的 VoiceOver label 查询,保留原等待和选中状态断言;新落盘回归与该 UI 用例在本地 iPhone iOS 27 模拟器 2/2 通过,后续 iOS 26.5 / Xcode 26.5 CI 已通过:iPhone、iPad 各 62 项单元与集成测试及 8 项 UI 测试,合计 140/140,零失败;两个新增或调整用例均在两端通过。CI run
40091580984,PR headf15c530,实际测试合并提交a40b461(基线cd91350)。首次查询失败的根因仍未证实,不将一次绿灯表述为已定位框架缺陷。
构建 6 内部分发与原位升级
候选 2.0.0 (6) 已在独立工作树完成 Release 归档、仅内部导出、IPA 签名 / 隐私清单 / Production 权限核验及 Apple VERIFY SUCCEEDED 校验。冻结源码 ddc007c81f99a2f923b1a6bf7a489be6156ee783 基于已测试的 f15c530,仅提高构建号;IPA SHA-256 为 2768034b0a4741e1a391231afc32bbdbc017319b17d2ef8d7edc06184d5e159f。与实际 CI 合并提交相比,原生业务源码和打包隐私、数据输入一致;CI 中构建号为 5,另有未用于此包的外测导出配置。
用户授权后已上传同一 IPA,构建 ID 为 06b01396-baad-4ce7-adff-c881e2584f98。服务端按精确构建回读为 VALID / INTERNAL_ONLY / IN_BETA_TESTING,关联原有内部组的 1 名成员,外部状态 NOT_APPLICABLE;未开放外测或邀请新成员。
- Mac 原位更新后,12:34 UTC 首次启动尚未进入私人菜谱页,两条历史冲突记录的正文由 396 / 412 字符自动清空,修改时间修正至删除时间;有效
102404的编号和内容完整保留。 - 12:43 UTC iPhone TestFlight 已显示
2.0.0 (6)和“打开”;设备信息与 Mac 安装包均核对为构建 6。完成 TestFlight 启动说明后正常进入做菜页,没有卸载或清库。 - 12:44 UTC USB 只读存储与 Mac 对照:两条历史冲突正文均为空,有效
102404的编号、鸡蛋及时间戳完全一致。界面也保留该菜谱。此项证明更新后保留正常记录及旧残留收敛,不替代修复构建的新一轮离线冲突验收。 - 12:47 UTC,用户在飞行模式且设置中 Wi-Fi 关闭后保存
240610(土豆),并给102404添加番茄。USB 存储确认新条目与修改均已落盘;Mac 仍无新编号,旧菜谱只有鸡蛋。隔离检查通过后,12:48 UTC 通过 Mac 界面删除102404,确认正文为空,手机仍保留离线修改和新菜谱。 - 重连后两端都停留在做菜页,未进入私人菜谱页:12:49 UTC 手机已收到删除,
102404正文为空,新240610完整保留;12:50 UTC Mac 收到相同新编号、内容和时间戳,两端删除记录的正文均为 0,修改时间等于删除时间,两条历史冲突正文也持续为空。本轮离线新增与并发修改 / 删除验收通过;没有用数据库写入或清库促成收敛。 - iPhone 终止并重新启动后,新菜谱仍完整保留,冲突删除未恢复。两端界面均只显示
240610;该编号与断网时的持久化证据完全一致。本次通过不能解释构建 4 的首次新条目缺失。 - 12:51 UTC Mac 给
240610添加番茄,iPhone 已打开的详情自动更新;12:52 UTC 两端存储的编号、土豆 / 番茄和修改时间完全一致。随后在手机删除,12:53 UTC Mac 已打开的详情自动变为“菜谱暂不可用”,两端有效私人菜谱数均为 0,测试条目的删除正文均为空。构建 6 的反向在线修改、删除及合成数据清理通过。 - 文档跟进提交
900f1a4的 CI run40093741462也已全部通过;实际测试合并381689d(基线9f753c6),iPhone / iPad 各 62 项单元与集成测试及 8 项 UI 测试,合计 140/140,零失败。构建 6 内部测试说明已更新并回读,区分本轮同步通过与仍未完成的发布门禁。
剩余发布门禁与数据收尾
以下清单保留生产测试结束时的状态。随后维护者将剩余验收调整为模拟器,具体替代覆盖见后续记录,不再以继续操作真机作为本轮收尾条件。
- 构建 6 的原位升级、旧残留清理及新一轮离线新增 / 修改 / 删除冲突复测已通过。首次构建 4 新条目缺失仍缺少可复现根因,继续保留调查记录,不能归因于已修复的删除正文问题。
- 在专用测试账户 / 设备上确认未登录、退出 iCloud 后本地数据保留及另一 Apple 账号隔离;本轮未退出用户现有 iCloud。
- 补齐第二端首次接收偏好前不编辑、接收后立即编辑的完整顺序证据,及离线收藏变更。已有双向前台偏好结果不替代所有首次启动竞争条件。
- 真机历史“清空”与两端通知授权 / 点击跳转尚未执行;历史只提供清空全部入口,因此没有用用户原有记录做删除试验。
最初构建 4 的测试私人菜谱与胡萝卜已从有效列表移除,收藏及偏好当时恢复基线。后续观察到 iPhone 临期提醒已开启,保留该用户状态。构建 5 跟进中已在 Mac 删除无后续用途的 102403;构建 6 原位升级确认有效 102404 保留、两条旧残留正文清空后,完成上述新冲突复测及 240610 清理。最终两端没有有效测试私人菜谱,合成删除记录仅保留必要删除标记,不保留正文。保留了一条烧鸡的测试浏览记录,因为 App 没有逐条移除历史的入口;原有五条历史没有清空。不能把构建 4、5 或 6 的结论直接套用到所有后续候选。