---
description: Ailoha iOS 从用户循环到运行组件、状态所有权与目标边界的递归架构
status: review_pending
updated: 2026-08-12
sources:
  - repo://ailoha-agent-iOS@5e6ac6e41c53780c85808c92290d2bd38f809123
confidence: high
sensitivity: internal
---

# 递归架构

## 如何阅读

每层回答不同问题，避免把页面树误当系统架构：

- **L0 产品循环**：用户为什么感知到价值。
- **L1 运行表面**：用户能进入的长期业务域。
- **L2 组合与状态 owner**：谁决定路由、生命周期和可观察状态。
- **L3 数据与事件路径**：REST、ARPC、App Group、EventKit 如何改变状态。
- **L4 热点与迁移缝**：重构必须先建立哪些契约。

状态标记：`active` 为当前可达路径，`dormant` 为 feature flag/注释/遗留路径，`target` 为建议边界。`fact` 直接由代码支持，`inference` 是由多条事实推出的判断。

## 2026-08-12 本地实现覆盖

本页的 current architecture 仍指固定基线，不把未合并候选伪装为运行事实。Wave 0 在其上建立了一个局部保护候选：

```text
AuthService.logout
  → AccountScopeResetCoordinator.reset synchronously
      ├─ Contact projection + in-flight generation
      ├─ Calendar projection/cache + cancellables
      ├─ Task routing/history/state + response handlers
      ├─ CompletedTaskCount
      └─ AttachmentCache
  → intended release identity / credential state
```

攻击审查表明这条图只覆盖“部分本地投影清理”，不能代表完整账户销毁合同：Home/Completed/TaskChat REST 迟到回写、Intent 网络/EventKit 副作用、Device registration 乱序 upsert、credential/raw purge 仍未受同一 owner 约束。因此 PRIV-001 仍是 P0 open，完整反例见 [Wave 0 Implementation Overlay](wave0-implementation.md)。

当前更准确的目标抽象是：

```text
AccountWorkLease(identity, processEpoch, crossProcessSession)
  ├─ validates in-memory commits
  ├─ binds REST / refresh / retry
  ├─ guards EventKit and device-registration effects
  └─ expires before raw/credential purge is verified
```

此 lease 必须与 durable raw purge、credential purge 的失败注入一起验证；仅在响应后丢弃 UI 回写，无法撤销已发送到 B 的 A 外部副作用。

## L0 — 产品循环

```text
理解截图 / 对话 / 联系人 / 日历上下文
  → 提议下一步或生成任务
    → 用户确认、编辑或拒绝
      → App / Widget / Live Activity 执行动作
        → Task/Contact/Calendar 状态收敛
          → 新事件继续触发理解
```

`fact`：当前代码已经覆盖截图捕获、Task Chat、联系人/日历卡片、Widget/Live Activity 动作、ARPC 实时事件和 REST 回写。

`inference`：系统的主要复杂度来自“同一用户意图跨多个进程、协议和临时 ID 收敛”，不是 SwiftUI 页面数量本身。因此目标架构应优先统一 state transition、account scope 和 effect 边界，而不是先按文件大小拆分。

## L1 — 八个运行表面

### 1. App shell 与身份

```text
AilohaApp
  ├─ pre-login composition: Auth / API / STT / Upload
  ├─ lifecycle: foreground/background/auth changes
  ├─ global sheets/covers/deep links/recovery
  └─ WindowGroup → RootPage
       ├─ unauthenticated → Login
       ├─ authenticated + onboarding incomplete → Onboarding
       └─ authenticated + onboarding complete → MainTabPage
```

- `active/fact`：`AilohaApp` 同时执行日志、Sentry、配置、Auth、ServiceLocator、截图、feature flag、App Group、deep link 与恢复初始化。
- `active/fact`：`RootPage` 再注入 Navigation/Sidebar state 并管理 import/report 等 sheet。
- `migration seam`：先把 `AppLifecycleCoordinator`、`AccountSession`、`AppDependencies` 从 SwiftUI Scene 中抽出，Root 只渲染 authentication state。

### 2. Onboarding

```text
OnboardingCore reducer (Package, 12 routes, tested)
  ↕ route/action projection
App OnboardingCoordinator (auth + retries + UI transitions)
  ├─ active → Name Setup / Permissions / Action Button / Try It / Welcome
  └─ dormant compatibility → questionnaire preload / answers / legacy step API
```

- `active/fact`：Package reducer有测试；当前 `OnboardingPage` 可达路径没有 questionnaire route，App coordinator 仍持有认证/API 与 UI 跳转语义。
- `dormant/fact`：questionnaire 预载、answers 与兼容 step API 仍在 coordinator，但其设计生命周期尚未由 design owner/successor 确认。
- `active/fact`：Try It 的消息、Dynamic Island、联系人和日历结果全部来自本地常量/模拟，不执行真实后端动作。
- `target`：Reducer 只管理 journey state；每个 step 通过 typed effect 调用外部能力；“演示”与“真实能力验证”在产品语义上分开。

### 3. Task / Chat / Home

```text
HomeViewModel ─ REST list/pagination/category/optimistic mutations ─ Home UI

TaskLifecycleCoordinator (facade)
  ├─ TaskCreationService
  ├─ TaskResponseService
  ├─ TaskInteractionService
  ├─ TaskHistoryService
  └─ TaskStateManager
       ├─ task detail store
       ├─ cid ↔ temp/server id mapping
       ├─ pending processing state
       └─ LRU / migration

TaskChatViewModel
  ├─ history + row projection + feedback
  ├─ ARPC topic subscription
  └─ V1 SwiftUI page / V2 UICollectionView page
```

- `active/fact`：Task 详情与 Home 列表是两个并行 state owner；因此飞书所称 TaskStateManager “single source”只覆盖部分 runtime。
- `active/fact`：Router 通过 RemoteFeatureFlags/DebugFlags 在 V1 与 V2 Chat 之间切换，默认服务端未开时回到 V1。
- `active/fact`：temp→server ID、pending 与 deep-link navigation 中存在固定 100ms timing workaround。
- `target`：`TaskRepository` 统一 canonical entity、ID alias 与 ordered transition；Home/Chat 是 query projection；V2 迁移要有观测、回滚和 V1 删除阈值。

### 4. Contact

```text
ContactManager.shared / ServiceLocator.contactManager
  ├─ list/search/pagination/sort
  ├─ detail/tasks/events caches
  ├─ cluster/meet merge state
  ├─ REST CRUD
  └─ ARPC merge
       ↓
ContactList + ContactDetailViewModel + edit/share/history pages
```

- `active/fact`：同一实例既通过 `.shared` 又经 locator 访问；Manager 持有 13 个 published 状态并混合多个业务子域。
- `active/fact`：列表 page 1 响应到达前有意保留旧数组；正常 logout 没有调用 `clearCache()`。
- `target`：Account-scoped `ContactRepository` 持 canonical contacts；list/search/detail/cluster 分为 query/session state；页层只发 intent。

### 5. Calendar

```text
CalendarViewModel.shared
  ├─ month/day/selection/editor UI state
  ├─ REST pagination + 5-minute range cache
  └─ events → eventsByDate projection

ExternalCalendarSyncService
  └─ backend authoritative → EventKit mirror
```

- `active/fact`：CalendarViewModel 的 `events` didSet 重算另一个 `@Published eventsByDate`；详情 view 与 sheet 都含直接 API/editor 逻辑。
- `active/fact`：`clearCache()` 只清 range cache，不清 published events；正常 logout 未调用它。
- `target`：Account-scoped event repository + calendar query cache；selection/editor 变成 screen session；EventKit mirror 是显式 effect，不能成为第二真相源。

### 6. ARPC 与 API

```text
AilohaARPCKit Session
  ├─ websocket / reconnect / ACK / heartbeat
  └─ transport events
       ↓
App ARPCService
  ├─ business handlers / subscriptions
  └─ reconnect → TaskLifecycleCoordinator resubscribe

Generated API façade → feature services / many direct View consumers
```

- `active/fact`：Package transport 和 App business handler已有物理分层，但重连后的业务重订阅仍集中耦合 Task coordinator。
- `active/fact`：多处 View 直接调用 `Api.*`，绕过可替换 feature effect/repository。
- `target`：transport 只发布 typed domain events；Feature store/repository 订阅自己的事件并负责 idempotency；UI 不直接依赖 generated façade。

### 7. Widget / Live Activity / Notification extensions

```text
App Intent / screenshot capture
  → SharedDataManager + SharedConstants (App Group)
  → pending action / pending intent task ledgers
  → upload + create task
  → ActivityKit content state
  → Widget/Live Activity confirm/edit/dismiss
  → App deep link / recovery
```

- `active/fact`：App Group 持有 screenshot path、auth/action/token 等敏感与恢复状态；logout 已显式清 Live Activity transient state。
- `active/fact`：Live Activity 视图 1682 行且没有 accessibility modifiers；复杂度来自多卡态、多动作和视觉分支，不只是行数。
- `target`：版本化 cross-process command/event schema、account/environment namespace、TTL 与 recovery contract；Live Activity 以 state×action matrix 拆组件。

### 8. Design system 与质量基础设施

```text
Figma node / manual design intent
  ├─ ALHColors.primaryAccent (#00C8B3 / #D6FE51)
  ├─ AilohaColorTokens.accent (#C8F04A)
  ├─ WidgetCardKit.figmaAccent / Tint
  └─ raw color + fixed font sizes
       ↓
128 DesignSystem importers + app/widget components
```

- `active/fact`：三个 accent 族及 raw literals 共存；ThemeManager 保存 preference 却固定返回 dark。
- `active/fact`：DesignSystem font adapter 使用 `fixedSize`，明确关闭 Dynamic Type。
- `active/fact`：App 无原生 test target；只有 Package tests，shared scheme testable 悬空。
- `target`：由一份 versioned semantic token manifest 生成 Figma mapping、SwiftUI/AppKit/Widget consumer；Hosted app test target验证 account lifecycle、route/state contract、a11y semantics 和迁移删除条件。

## L2 — 当前控制面交叠

| Concern | 当前 owner | 交叠/缺口 | 目标 owner |
|---|---|---|---|
| 认证与账号生命周期 | AuthService + AilohaApp cleanup + Settings delete flow | 正常 logout 与 account deletion 清理集合不同 | `AccountSession` + `AccountScope.reset()` |
| 依赖组合 | ServiceLocator + `.shared` + exported imports | 生命周期与依赖在运行期隐式决定 | typed `AppDependencies` / FeatureDependencies |
| Task canonical state | TaskStateManager + HomeViewModel + Chat VM | list/detail/topic/temp ID 分散 | Account-scoped TaskRepository |
| 导航 | Router + NavigationUIState + NotificationCenter observers | stack/visible chat/deep link 时序补偿 | typed AppRoute + single navigation reducer |
| 设计 token | ALHColors + AilohaColorTokens + WidgetCardKit + literals | 值与语义不一致，无 revision contract | generated semantic tokens |
| 跨进程恢复 | SharedConstants + SharedDataManager + Activity services + intents | 多 ledger、TTL/account namespace 不统一 | versioned CrossProcessStore |

## L3 — 关键数据流

### Task/Chat 收敛

```text
user input / screenshot
  → create request (cid/temp id)
  → REST response (server task id)
  → TaskStateManager alias migration
  → ARPC topic message / processing status
  → TaskChat row projection + Home list projection
  → Widget/Live Activity reconciliation
```

必须守住的合同：同一个 cid 只映射一个 server ID；重复事件幂等；topic 在重连后恢复；Home 与 Chat 对同一 transition 得到一致投影；失败时能回滚或显示可恢复状态。

### Account lifecycle

```text
Account A authenticated
  → process-global Contact / Calendar / Task state populated
  → logout clears tokens +部分缓存/服务
  → Account B authenticated
  → singleton owners may immediately reuse old published arrays / task correlation state
  → new request/cache hit eventually changes—or may retain—old projection
```

这里的目标不是“多调几个 clearCache”，而是令所有 account-owned state 由同一 scope 构造、取消任务、清空内存/磁盘/App Group、再销毁。编译/测试应能枚举 scope-owned resources。

## L4 — 建议的目标边界

```text
AppShell
  ├─ AppLifecycleCoordinator
  ├─ AccountSession
  │    └─ AccountScope
  │         ├─ TaskFeature (Repository + Store + Effects)
  │         ├─ ContactFeature
  │         ├─ CalendarFeature
  │         └─ ConversationFeature
  ├─ NavigationStore
  ├─ CrossProcessStore (versioned, namespaced, TTL)
  └─ PlatformServices
       ├─ API transport
       ├─ ARPC transport
       ├─ Analytics / redacted logging
       └─ EventKit / ActivityKit adapters

DesignSystem
  ├─ generated semantic tokens
  ├─ Dynamic Type typography
  ├─ native semantic controls
  └─ app/widget component contracts
```

这不是要求一次性换成某个架构框架。关键是不变量：owner 唯一、effect 可替换、account scope 可销毁、跨进程消息可版本化、UI 只消费投影、旧路径有删除合同。具体迁移见 [重构方案](refactor-strategy.md)。
