---
description: Ailoha iOS 前端按机制去重的问题目录、证据、验证与修复边界
status: request_changes
updated: 2026-08-28
sources:
  - repo://ailoha-agent-iOS@5e6ac6e41c53780c85808c92290d2bd38f809123
  - repo://ailoha-agent-iOS@6922f359ad08da70242e4ec0c5c4a1486bc2908d
confidence: high
sensitivity: internal
---

# 问题目录

## 使用方式

这里的“一条问题”是一个可独立验证、修复和回滚的运行机制，不是一个正则命中或一个大文件。优先级口径：P0 可能跨账户泄露或暴露 credential；P1 会系统性阻碍用户结果、生产可信度或安全重构；P2 是应在相邻切片中治理的结构债务。`fact` 由当前修订直接支持，`inference` 是由事实推出、仍需运行证据校准的判断，`hypothesis` 必须通过指定动作验证。

P0/P1 的表格保留问题 kernel 和代表锚点；它们在 Release、main、本地候选上的 `applies_to / observed_at / invalidated_by / owner role / verifier state` 以 [Revision Claim Ledger](revision-aware-claim-ledger-2026-08-27.md) 为准。没有绑定 canonical Linear issue 的条目不声称已有人负责。

## Wave 0 implementation overlay · 2026-08-12

下表的机制与源码锚点是固定基线发现，不因本地修复而改写。隔离分支 exact 87-file manifest `45ba8781…e44c` 的独立 verdict 为 `request_changes`（0 P0 + 1 P1）；Calendar logout/reset P1 已关闭，credential regex inventory 仍有同根邻接绕过：

| Issue | 本地状态 | 新证据 | 尚未取得 |
|---|---|---|---|
| PRIV-001 | independently closed on predecessor / regression pending | 真实 task pre-send/direct cancel、reset tombstone、Calendar backend-ID/phase ledger + exact GET recovery + serial owner；所有 backend DELETE（含无 Apple link）前持久化 owner。Logout 在同一 serial owner 内先结算 pending backend outcome，再提交 journal/session revoke；generic reset 对未知 outcome fail closed；hosted 65/65 + account/effect 89/89 | `987a1b8b…d739` exact review 已关闭 Calendar handoff P1；current exact regression review 后仍需 UI replay/CI/rollout |
| SEC-001 | exact request_changes / one P1 | 生产 callsite value-free；跨文件 function/property/closure-return conservative summary、常见 Logger grammar、Logger typealias fixed point、简单 Optional wrapper 与 multi-hop wrapper；credential 40/40 | 括号 typealias target、`Optional.some` 与 typed closure receiver 仍可绕；改用 SwiftSyntax/AST 或定义有限版本化 syntax contract，protected CI 前不外推为语言级完整污点分析 |
| PRIV-002 | local component candidate | Calendar account reset 同步清 published projection、cache、selection、task/cancellable | 不外推为完整 AccountScope approval |
| TEST-001 | exact local receipt / implementation request_changes | hosted target/test plan；65/65 覆盖 pre-send/direct cancel、journal、crash-before-bind、same-session relaunch、startup/foreground create/delete concurrency，以及 linked/backend-only delete response-lost / logout settlement；129/129 枚举 guards | 绿测不关闭未枚举 grammar；后续再接 SwiftSyntax/AST 或有限 syntax contract、真实 UI/extension/system surfaces 与 protected CI |

完整实现与 recovery 边界见 [Wave 0 Implementation Overlay](wave0-implementation.md)。

## P0 — 先建立保护边界

| ID | 机制与结果 | 类型 / 置信度 | 代表证据 | 验证与最小修复边界 |
|---|---|---|---|---|
| PRIV-001 | **普通注销只清了部分账户态。** `AilohaApp.cleanupAfterLogout()` 已通过 `TaskLifecycleCoordinator.resetRoutingStateForLogout()` 同步重置 Task routing/state，也清 Import、Live Activity 等状态；但 Contact 与 Calendar 的 published state 没有同步清理，各服务的迟到异步结果也没有共同的 session / generation fence。账号 B 仍可能在第一帧或旧请求回包后看到账号 A 的联系人、日历投影或其他迟到副作用。 | fact + inference / high | `AilohaApp.cleanupAfterLogout()`；`TaskLifecycleCoordinator.resetRoutingStateForLogout()`；`TaskStateManager.resetForEnvironmentSwitch()`；`ContactManager.loadContacts()`；`CalendarViewModel.clearCache()` | 先写 A→logout→B hosted app test，分别断言第一帧和旧请求回包后无 A 数据。保留现有 Task reset；最小修复是补 Contact/Calendar 同步 reset，并给所有迟到工作加统一 session / generation fence，不是在某个页面 `onAppear` 清数组。 |
| SEC-001 | **credential 前缀绕过现有 redactor。** APNs token 前 20 位、Soniox API key 前 8 位、截图 update token 前缀被构造成普通字符串；ALHLogger 脱敏后仍落盘并进入周期日志。SharedConstants 还存在一条不经过 ALH redactor 的原生 Logger 表面。 | fact / high | `AppDelegate.swift#L150`；`RealtimeTranscribeService.swift#L92`；`ScreenshotIntentRunner.swift#L152`；`ALHLogRedactor.swift#L45`；`ALHLogger.swift#L112,#L336`；`SharedConstants.swift#L253,#L790` | credential 字段一律不写值或前缀；增加精确消息 redaction tests 与日志 sink test。OSLog 的 retention/export 单独核验，不把它与周期上传混为一谈。 |
| XPROC-001 | **跨进程信封未约束不可逆副作用。** 旧 Screenshot/Live Activity Intent 可在 session rotate 后使用 B 凭据执行 A 网络或 EventKit 动作；App Group writer 也可跨过 rotate/clear 再落盘。 | fact + inference / high | `ScreenshotIntentRunner.swift`；`ScreenshotAPIClient.swift`；`LiveActivityIntents.swift`；`SharedDataManager.swift` | Versioned account/session envelope 必须绑定 transport 与 system effect，并在写前/后重验；用 network/EventKit spy 与 suspended raw writer 证明旧工作不能落地。 |

## P1 — 重构前必须进入计划

| ID | 机制与结果 | 类型 / 置信度 | 代表证据 | 验证与建议边界 |
|---|---|---|---|---|
| PRIV-002 | `CalendarViewModel.clearCache()` 只清 range cache，不清 `events`/`eventsByDate`；即使删除账号路径调用它，已发布投影仍可能存活。 | fact / high | `CalendarViewModel.swift#L27,#L51,#L469-L471` | `resetAccountState()` 同步清可观察投影、缓存、选中项和运行任务；双账号测试断言第一帧为空。 |
| STATE-001 | Task 详情/alias/pending 由 `TaskStateManager` 持有，Home 的分页、分类、REST merge 与乐观更新由 `HomeViewModel` 独立持有；“Task 单一真相源”只在局部成立。 | fact / high | `TaskStateManager.swift`；`HomeViewModel.swift`；`TaskLifecycleCoordinator.swift` | 以 `TaskRepository` 统一 canonical entity、ID alias、ordered transition；Home/Chat 只持 query/UI projection。 |
| NAV-001 | Task deep link 由 NotificationCenter 驱动，先看 `visibleTaskChatId`，固定 sleep 100ms 后再看并 `popAll`/push；页面实例 owner、栈深度和自愈逻辑共同补偿生命周期时序。 | fact / high | `MainTabPage.swift#L49-L75`；`NavigationUIState.swift#L33-L76` | 建 typed `NavigationEffect` 队列；同 route 去重、replace-stack、恢复都用确定性 reducer test，删除固定延时。 |
| ARCH-001 | `ServiceLocator` 用 `[String: Any]` 注册服务，缺失时 `fatalError`；同时大量 `.shared` 绕过 locator。生命周期、账户边界和测试替身由运行时隐式决定。 | fact / high | `ServiceLocator.swift#L16-L41,#L112` | 新功能只接受 typed dependencies；旧 singleton 通过 adapter 接入，按 feature 删除注册项。 |
| TEST-001 | Xcode 工程没有原生 app test target，共享 scheme 仍引用不存在的 `ailoha-assistant-sharedTests.xctest`；Package tests 无法覆盖账户、导航和多 target 集成合同。 | fact / high | `Ailoha.xcscheme#L33-L43`；工程快照 | 新建 hosted app tests，先覆盖 PRIV-001、NAV-001、Task alias/乐观更新和 App Group migration；修复 dangling scheme。 |
| A11Y-001 | DesignSystem 的 Lora/Manrope adapter 使用固定 size，而扫描未发现 `@ScaledMetric`；Dynamic Type 不能系统性传入品牌排版。固定 frame 进一步放大裁切风险。 | fact + inference / high | `AilohaTypography.swift#L63-L91`；源码扫描 236 个固定 SwiftUI font 信号、0 个 scaled metric | 改为相对 text style 或等价可缩放字体；AX1–AX5 + 中文/英文截图和截断断言作为门禁。 |
| A11Y-002 | Calendar 的核心移动/缩放操作依赖 LongPress + DragGesture，没有对应 accessibility action；Home 部分 segment 也是 Text + tap gesture，而非原生 Button 语义。 | fact / high | `DraggableEventView.swift#L45,#L120-L154`；`HomePage.swift#L175` | 为移动/调整时长提供命名 action 与替代编辑入口；VoiceOver 实机走查 rotor、焦点和完成反馈。 |
| A11Y-003 | Release 的 Swift 源扫描是 0 处 Reduce Motion 读取；main 增加了 2 处读取，但 active Onboarding 的循环视频、引导视频和 Try It `repeatForever` 呼吸仍不读取该偏好。main 是“部分覆盖”，不是 Release/main 都已有覆盖。 | fact / high | `Release@0b90745` Swift search：0；`main@6922f359`：`ALHViewExtensions.swift`、`GuidePopView.swift` 读取偏好；OnboardingKit `LoopingVideoPlayerView.swift`、`OnboardGuideVideoView.swift`、`TryItChatMock.swift`、`TryItOverlays.swift` 未读取 | 给 active 自动媒体与重复动效提供静态 / 低动 fallback；分别在 Release/main 开启 Reduce Motion 跑完整流程，不混用两条代码线的计数。 |
| DS-001 | Release 仍是多源颜色常量；main 已建立 `colors.tokens.json → generated Swift`，Widget 开始消费，但 `ALHColors` 仍手写 adaptive brand colors，因此是部分迁移而非“完全没有共同来源”。 | fact / high | `Release@0b90745` token 常量；`main@6922f359` manifest / generated Swift / `ALHColors.swift` | 先定义语义角色；补生成确定性、legacy consumer 账本和 App / Widget 视觉回归；consumer=0 后删除旧来源。 |
| PROD-001 | Onboarding Try It 的聊天、Dynamic Island、联系人和日历结果来自本地 mock；它能教学，却不能证明首次真实能力可用。 | fact / high | `TryItChatMock.swift`；`TryItOverlays.swift#L328`；`OnboardTryItChatPage.swift` | 产品确认“演示”或“真实价值验证”。若保留演示，明确标识；另补一个低风险、可恢复的真实闭环。 |
| PROD-002 | Figma 五选项问卷不是当前固定修订的 runtime 主路径；代码仍预载问卷并保留兼容 step API。该设计节点的生命周期与 successor 尚未由 design owner 确认。 | fact / high（runtime）+ unknown（design lifecycle） | `OnboardingFlow.swift#L4-L15`；`OnboardingPage.swift#L86-L177`；Figma `2391:25876` | 先确认节点是 current、dormant 还是 superseded；再为代码兼容路径写删除条件，避免把像素还原任务误当当前功能。 |
| PERF-001 | `ContactDetailViewModel` 有约 36 个 published wrapper，并把四个 child state 的 `objectWillChange` 转发到 root；`ContactNotesView` 观察整个 VM。Debug Simulator 中 12 次姓名输入稳定触发 12 次 Notes body、12 次全量备注解析和 12 次日期分组；1,000 / 5,000 notes 的每次无关投影中位数约 23.6 / 91.5ms。 | fact（代码与 Debug 页面路径）+ unknown（真机 / 生产影响）/ high | `main@6922f359`：`ContactDetailViewModel.swift#L20-L94,#L209-L227,#L1037-L1040`、`ContactDetailPage.swift#L15-L23,#L113-L123`、`ContactNotesView.swift#L19-L65,#L77-L123`；`contact-detail-typing-debug-simulator-summary.json` | P0 工程切片：编辑草稿归 editor-local owner，Notes 只消费不可变 input/projection，consumer test 后逐个删除 child→root forwarding；20 次无关输入的 Notes projection 必须为 0，且 full grouping oracle、Widget New 分组与真机 Release Gate 同时通过。 |
| PERF-002 | Calendar `events.didSet` 同步遍历分组/逐日排序，再发布 `eventsByDate`，形成派生计算和二次 observation fan-out。 | inference / medium-high | `CalendarViewModel.swift#L27-L30,#L51,#L173` | 用 production-sized fixture 做 signpost + SwiftUI trace；按稳定 query/cache key 计算并只发布受影响日期。 |
| MIG-001 | 当前 Release 仍由 flag 在 Chat V1 / V2 之间选择；current main 已删除 V1 与 debug flag，只构造 V2，旧 consumer=0 的结构事实成立。main 的结构迁移已关闭，但 V2 长会话、崩溃 / 回滚基线和进入 Release 的证明仍缺。 | fact / high | `Release@0b90745` Router / `ChatPageDebugFlags`；`main@6922f359` Router / tree | Release 定义 rollout / 删除门；main 用静态 guard 防止旧 consumer 再引入，并补长会话性能与失败基线；不再给 main 排“删除 V1”。 |
| API-001 | 本地生成 API client、App 内 façade 与部分 View/VM 直接网络调用并存，错误、鉴权、重试和 mock 边界不统一。 | fact / high | `Packages/AilohaAPIClient`；`APIService.swift`；各 feature 的 `Api.*` consumer | UI 只依赖 feature protocol；transport gateway 统一 auth/error，不强制一次性重写 generated client。 |
| OBS-001 | `AppRoute.trackName` 对 task chat 做了低基数化，但 share/contact/cluster 等 route 仍拼接实体 ID；随后写日志和 Sentry breadcrumb。 | fact / high | `Router.swift#L147-L151`；`NavigationUIState.swift#L19-L20` | 事件名保持低基数，实体标识只在有授权、可脱敏字段中使用；加 cardinality/privacy tests。 |
| REL-001 | ARPC transport 与 App handler 已分层，但重连后的业务重订阅集中耦合 Task lifecycle；重复事件的幂等边界依赖多个 owner 协同。 | fact + inference / medium-high | `AilohaARPCKit`；`ARPCService.swift`；`TaskLifecycleCoordinator.swift` | 用断线/乱序/重复事件 fixture 验证各 repository 自有订阅与幂等；transport 不直接知道 feature。 |
| REL-002 | **Setup 2.0 只让新用户填姓名，却仍按旧三题问卷的 version 提交。** current main 的可见页面只能积累 `fullName`；bundled fallback 同时把 `personaType`、`painPoint`、`fullName` 标成 required，OpenAPI 又声明服务端校验 required。客户端与本地配置的合同缺口已证明；当前远端配置和真实服务端是否拒绝仍未知。 | fact（本地合同）+ inference（真实账号可能被阻断）/ high | `main@6922f359`：`OnboardingFlow.swift#L4`、`OnboardingPage.swift#L86`、`OnboardingCoordinator.swift#L253,#L355`、`OnboardingQuestionnaireFallback.json`、`http_api_v1.yaml#L6883`；[完整调查与验收包](current-main-onboarding-design-contract-gap-2026-08-28.md) | 先用去敏 DEV GET/POST 裁决真实行为；姓名页使用独立 name-only schema/endpoint，或恢复真正可见的 required 问卷。增加 required-field closure guard：每个 required field 必须 rendered、authoritative-prefill、explicitly-skippable 或从 schema 移除。 |

## P2 — 随垂直切片治理

| ID | 机制与结果 | 类型 / 置信度 | 代表证据 | 验证与建议边界 |
|---|---|---|---|---|
| FLAG-001 | `chatPageV2` 存在 `UserDefaults.standard`，网络失败会沿用旧值；它没有 account/environment namespace，切换环境后可能继续沿用旧环境 flag。 | inference / high | `RemoteFeatureFlags.swift#L20-L29,#L57-L61` | flag key 加 environment/version；失败策略显式，环境切换测试断言不串值。 |
| THEME-001 | ThemeManager 保存 system/light/dark preference，却固定返回 `.dark`；设置合同与运行结果矛盾。 | fact / high | `AppThemeManager.swift#L32,#L54-L55` | 产品决定 dark-only 就删除伪选项；支持主题则实现 token parity 与快照测试。 |
| PERF-003 | 多图输入先整批形成 `[UIImage]`，附件状态继续保留原图；每张图的 detached 预检会丢弃压缩产物，上传前再压一次。三进程 Simulator 机制实验中，10×48MP current-style 峰值增量中位数 785.1MB，一次 prepare 文件化候选 82.0MB；释放后两边都回落，因此当前证据支持瞬时工作集放大，不支持永久泄漏或线上 OOM。 | fact（代码与 Simulator 机制）+ unknown（真机 / 生产影响）/ high | `main@6922f359`：`InputPanel.swift#L299-L321`、`CameraPage.swift#L59-L81`、`TaskLifecycleCoordinator.swift#L1058-L1079`、`TaskAttachmentManager.swift#L173-L193`、`FileUploader.swift`；`photo-import-three-run-summary.json` | P0 工程切片：统一 file-backed `PhotoImportPipeline`，逐张发布，600px preview + 2048px upload file 只编码一次，并发从 1 起步；真机 Release Gate 要求峰值至少降 60%、取消后回收，再判断是否升级生产严重度。 |
| PERF-004 | Chat V2 已有稳定 ID、同 tick 合并、后台 freeze 和滚动锚点，但任一非等价 `TaskData`、单次 feedback 或 expansion 仍从完整 history 重建全部 rows；renderer 随后再次全量扫描并生成 snapshot。5000 行 Debug Simulator 单行追加 3 次的投影 + 同步列表路径配对中位数约 81.9ms；优化编译的 production projector + renderer-preparation 机制实验约 8.49ms。 | fact（代码、逻辑机制、Debug Simulator 页面）+ unknown（真机 Release / 生产影响）/ high | `main@6922f359`：`TaskChatViewModel.swift#L34-L50,#L128-L185`、`ChatRowItem.swift#L101-L220`、`ChatCollectionView.swift#L510-L651`；`chat-projection-five-process-summary.json`；`debug-simulator-chat-update-summary.json` | P0 工程切片：保留 full build oracle 和现有列表保护，先为 feedback / expansion / processing 建 change + patch 窄增量，再做有安全边界的 append；真机 Gate 要求等价、5000 行 app-owned 主线程 p95 ≤16.7ms、相对下降至少 60%，否则 fallback。 |
| ARCH-002 | 1904/1786/1682/1568/1541 行热点分别混合历史、状态、API、UI 与兼容分支；行数本身不是问题，但责任浓度提高变更半径。 | inference / medium-high | 快照 hotspots；`TaskHistoryService`、`ContactManager`、`ScreenshotTaskLiveActivity` 等 | 只沿已定义 owner/effect/component seam 拆；每次拆分保持行为测试和可回滚 adapter。 |
| ARCH-003 | current main 把六个新增 Package 作为 App framework products，同时由 `GlobalImports.swift` 统一 `@_exported import`；任意 App 文件可以隐式消费 feature module，caller 旁边看不见依赖，Package seam 容易被误读成已完成的架构边界。 | fact（全局暴露与 caller）+ inference（认知 / 演进成本）/ high | `main@6922f359`：`GlobalImports.swift#L11-L16`；App target product references；Task / Import / TwoMeet / Speech / Markdown / Logging 的具体 caller | 先做 `AilohaTaskCore` 模块级显式 import pilot；盘点 Task services、Home 和 Chat caller，再比较编译错误、循环依赖、增量编译与 reviewer 理解成本。只有收益成立才推广和加 lint。 |
| STATE-002 | `ContactManager` 同时持列表、搜索、分页、详情、事件/任务 cache、cluster/merge 与 ARPC 更新，query/session/canonical state 混合。 | fact / high | `ContactManager.swift`（1786 行、13 个 published 信号） | 先抽 repository protocol 与 canonical store，再拆 list query、detail query、merge session；不做纯文件搬家。 |
| DOC-001 | 飞书 iOS 文档停在 2026-06-22；Figma 问卷节点不是当前 runtime 主路径且设计生命周期未确认；代码已包含 Setup 2.0、Try It、Chat V2 和三 Tab。三个来源没有共同 revision tuple。 | fact / high（时间与 runtime）+ unknown（design lifecycle） | 飞书 iOS parent + 10 子节点；Figma node；当前代码 | 建 surface revision tuple：product doc、Figma node、commit/flag、验证、owner、successor。 |
| A11Y-004 | 扫描未发现 `accessibilityDifferentiateWithoutColor`、focusable/keyboard shortcut；这不自动证明每个页面失败，但表示横切策略缺位。 | fact + inference / medium | 441 文件静态审计 | 对选中、完成、错误三类状态做无颜色/VoiceOver验证；iPad keyboard 支持按产品范围决定。 |

## 不应作为独立问题的信号

- 63 个 singleton、92 个 NotificationCenter、153 个 UserDefaults 命中是检索入口，不是 308 个缺陷。
- Formatter、GeometryReader、ForEach `id: \.self` 或大文件都需要沿更新频率、稳定 ID 和调用路径解释。
- Package 测试存在是积极基础；问题是缺 hosted app contract，不是“完全没有测试”。
- Chat V2、Onboarding reducer、typed `AppRoute`、ARPC Package 分层都是可复用迁移支点，不应在重构中抹掉。

## 决策顺序

1. 立即关闭 PRIV-001 / SEC-001 的暴露窗口并建立回归测试。
2. 用 STATE-001 + NAV-001 作为 Task 垂直切片，证明目标边界可以演进替换。
3. 并行建立 A11Y-001、DS-001、TEST-001 三条横切 guardrail。
4. 只有真机 Release trace 或生产证据支持时，才把 PERF-001/002/003/004 升为用户可感知性能缺陷；Debug Simulator 已足够把 PERF-001 从“可能 fan-out”升级为“页面路径真实 fan-out”，但不升级产品严重度。
