---
description: Ailoha current main 的 Live Activity 卡片数量、锁屏披露与系统表面动作边界决策包
status: proposed
updated: 2026-08-28
sources:
  - repo://ailoha-agent-iOS/revision/6922f359ad08da70242e4ec0c5c4a1486bc2908d
  - https://developer.apple.com/design/human-interface-guidelines/live-activities
  - https://developer.apple.com/documentation/widgetkit/creating-a-widget-extension
  - https://developer.apple.com/documentation/widgetkit/adding-interactivity-to-widgets-and-live-activities
  - repo://ailoha-brain/reports/ios-frontend-refactor-map/current-main-live-activity-transfer-contract-2026-08-28.md
  - https://app.notion.com/p/3c9a444a6c00815c8cb1d9807f49dce8
confidence: high
sensitivity: internal
---

<!-- brain-capture:ios-live-activity-product-policy-decisions -->

# Ailoha 灵动岛还有两件事不能交给代码默认值

## 先说人话

当前代码已经能把很多任务信息放到灵动岛和锁屏上，也能同时留下多张真实任务卡。但服务端和 App 只维护一个可变 update token 与一个观察任务。于是现在有两个没有明确 owner 的产品决定：

1. **同一账号的一台设备，究竟允许同时出现几张 Ailoha 任务卡？**
2. **锁屏、Always-On 和 Dynamic Island 究竟允许透露什么、允许直接做什么？**

这不是两个小 UI 参数。第一项决定 token registry、任务排队、抢占和 deep link 身份；第二项决定 payload schema、文案、按钮、安全边界和用户信任。本页把 current main 的事实、三个候选方案、推荐候选、反例和验收拆开。**推荐不等于已接受；需要产品 owner 在共同画布上做选择。**

共同决策入口：[Notion 决策页](https://app.notion.com/p/3c9a444a6c00815c8cb1d9807f49dce8)、[可视化预览](live-activity-product-policy-workshop-2026-08-28.html)与[可编辑 Excalidraw 源文件](live-activity-product-policy-workshop-2026-08-28.excalidraw)。机器审计证据：[live-activity-product-policy-audit.json](evidence/current-main-live-activity-product-policy-2026-08-28/live-activity-product-policy-audit.json)。

## 证据边界

- **fact：** 以下代码事实固定在 `6922f359ad08da70242e4ec0c5c4a1486bc2908d`，观察日期为 2026-08-28；审计涉及的文件与该 revision 一致。
- **inference：** 当前实现缺少一份统一的 cardinality / disclosure / direct-action policy；这会使代码局部选择替代产品选择。
- **hypothesis：** 用户已经发生隐私伤害、多卡在生产中必然丢更新、或用户一定偏好推荐方案，都没有被当前证据证明。
- **decision required：** 允许几张卡、是否保留常驻 idle、锁屏详情默认值和哪些动作必须回 App，需要具名产品 owner 接受后才能正式化。

## Current main 已经证明什么

### 卡片数量与寻址

- `TaskType` 当前覆盖 `calendar / contact / reminder / twomeet / send_invitation / task_created / unknown`。
- 一张 Activity 的静态 attributes 绑定 `actionId + taskId`；状态里又带 `taskId + widgetId`。这说明当前真实卡的语义是“**一张卡绑定一个任务**”，不是可随意换任务的通用槽位。
- 前台 reconcile 允许多张非 success 真实卡继续存在，只让旧 success 卡让位。
- `DeviceRegistrationService` 只有一个 `updateToken`、一个 `updateTokenTask`；每次接管新 activity 时会取消旧 observer。
- `RegisterDeviceRequest` 只上传一个 `update_token + activity_id`。
- App 已经支持 push-to-start token，项目 deployment target 是 iOS 18.0；常驻 idle placeholder 不再只是“必须兼容 iOS 16”的唯一办法，而是一项可以重新选择的产品/架构策略。

**结论：** 当前是“系统允许多卡、寻址与更新通道更像单卡”的混合合同。它证明结构矛盾，不证明生产事故已经发生。

### 披露与动作

`LiveActivityExtractedData` 有 21 个字段，其中包括：

```text
title / description / location / attendees
name / telephones / emails / company / jobTitle / notes
dueDate / note / contactName / clusterDisplayName
```

当前 Lock Screen / expanded 真实读取并显示：

- Calendar：标题、日期时间、地点；
- Contact：姓名、公司、邮箱、电话，更新时还会显示 notes；
- To Meet：地点、联系人和 note；
- Send Invitation：姓名、公司、邮箱和时间；
- Reminder / Task Created：标题或任务文字。

Widget 源码中没有发现 `privacySensitive`、自定义 privacy redaction 或独立 `DisclosurePolicy`。Pending Confirm 还提供 `Add / Update / Delete` 直接确认按钮；Send Invitation 已采用更安全的 `Review → App` 路径。

Apple 官方说明 Live Activity 会显著出现在 Lock Screen 与 Always-On，旁观者可能看见；对敏感内容应显示无害摘要，或让用户控制敏感信息显示并提供 redaction。Apple 还建议多事件跟踪优先用一张动态活动，而不是让用户在多张活动之间跳转。这些是外部约束，不会替 Ailoha 自动完成产品选择。

## 决定 1｜同一设备允许几张任务卡

### 要兑现的用户承诺

> 用户一眼知道 Ailoha 此刻在替自己跟进哪件事；新任务不会悄悄抢走旧任务，也不会出现两张卡却只有一张能继续收到更新。

### Option A｜每个任务一张卡，多卡并行

**适合条件：** 用户确实会同时主动跟踪多个独立、持续较久的任务，并愿意在系统表面切换。

**必须付出的成本：**

- update token 从单槽升级为 `account × installation × environment × task × activityInstance` registry；
- 后端分别寻址、重试、撤销、过期和审计每张卡；
- App 必须为每张卡保持精确 observer / reconcile / pending-end；
- 多卡排序和系统折叠由 iOS 决定，用户注意力会被切碎。

**反证：** 真实用户通常只关心“现在最需要我的一件事”，第二张卡只是噪音；或任一链路仍使用全局 scalar token。

### Option B｜一张 task-bound Spotlight Card，其他任务留在 App 队列

**推荐候选，但尚未接受。**

同一 `account × installation × environment` 最多一张真实任务卡；Activity 从创建到结束始终绑定同一 `taskId + activityInstanceId`，不能在同一实例内偷偷换任务。其他任务进入 App 内队列或普通通知，不在系统表面堆卡。

建议的选择规则：

1. `resolve` 高于 `review`，`review` 高于仅观察的 `observe`；同优先级按进入队列顺序。
2. 当前是 `review / resolve` 时，新任务不能静默抢占。
3. 当前只是 `observe`，新 `resolve` 可以通过一次明确 alert 结束旧卡并启动新卡；必须留下 `preempted_by_higher_attention` receipt。
4. 当前卡 terminal、用户显式 dismiss 或会话撤销后，再从队列选择仍然相关的下一项。
5. 队列为空就没有 Live Activity；不为获取 token 常驻一张 `0 tasks` idle 卡。

**为什么目前更合适：** 它符合 Ailoha“幕僚帮你筛出最值得注意的一件事”的产品感觉，也与 Apple 的单活动多事件偏好方向一致；同时它最接近现有单 token 事实，但要求把“选择哪一项”的 owner 从偶然代码顺序提升成正式 policy。

**反证：** 双任务真机测试表明用户必须同时看到两个独立进度，或者 Spotlight 卡频繁抢占导致理解成本高于多卡。

### Option C｜一张永久 Attention Session，内容在任务间轮换

静态 attributes 只绑定 `attentionSessionId`，当前 task 变成可变 ContentState。它理论上最像一个持续存在的系统级 Ailoha，但会放大 stale action 风险：旧截图、旧 deep link 或晚到事件可能命中新任务。

除非先有一次性 action receipt、严格 revision、任务切换动画/文案和可恢复队列，否则不建议作为第一刀。它是 L2 长期候选，不是 L1 修复。

### 建议先选什么

```yaml
decision: cardCardinality
status: PROPOSED
recommended: one_task_bound_spotlight_card
notAcceptedYet: true
needsOwnerChoice:
  - 是否取消常驻 idle placeholder
  - resolve 是否可以显式抢占 observe
  - 同优先级使用 FIFO 还是产品优先级
```

## 决定 2｜系统表面能透露什么

### 先按信息性质分级

| 等级 | 人话 | 示例 | 推荐默认 |
| --- | --- | --- | --- |
| `D0 operational` | 只说明 Ailoha 在做什么、要不要回来 | `Working`、`Needs review`、`Update delayed`、任务类别 | 所有系统表面允许 |
| `D1 bounded context` | 有帮助但可能暴露行程或关系的上下文 | 日期时间、通用地点类别、任务短标题 | 第一版不放；未来明确 opt-in |
| `D2 identity` | 能识别人或组织 | 姓名、公司、职位、会议对象 | 默认锁屏/Always-On 禁止 |
| `D3 raw/private content` | 原始私人内容和联系方式 | 邮箱、电话、地址、参与人、notes、原话、精确地点、URL | 永不进入 Activity payload |
| `D4 secret/control` | 控制系统的内部能力 | token、账号 ID、可复用授权 | 永不展示；opaque route receipt 也要回 App 重验 |

### 推荐的第一版：严格无害摘要

| Task type | 当前可能显示 | Lock Screen / Always-On 默认只显示 | 允许动作 |
| --- | --- | --- | --- |
| Calendar | 标题、时间、地点 | `Calendar change needs review` | `Open review`；不直接 Add/Update/Delete |
| Contact | 姓名、公司、邮箱、电话、notes | `Contact change needs review` | `Open review`；不直接写联系人 |
| Reminder | 标题、due date | `Reminder needs review` | `Open review` |
| To Meet | 地点、联系人、note | `Relationship update needs review` | `Open review` |
| Send Invitation | 姓名、公司、邮箱、时间 | `Invitation draft needs review · Nothing sent yet` | 维持 `Review → App`；不在系统表面发送 |
| Task Created / needs more info | 用户任务标题或说明 | `Ailoha needs more information` | `Open task` |
| Running / stale / failed | 阶段、计时、错误 | `Ailoha is working` / `Update delayed` / `Open Ailoha` | Open；业务取消需单独确认 |

Minimal 与 compact 永远只用 `D0`。Expanded、Lock Screen、StandBy 和 Always-On 第一版也只用 `D0`；完整上下文留在 App。以后若有真实用户证据支持，可以在独立 opt-in 下把部分 `D1` 标成 `.privacySensitive()`，并对 `.privacy` redaction 提供自定义无害摘要。不要把系统“可能自动遮挡”当成产品默认。

### 为什么系统表面的确认动作也属于这个决定

即使 iOS 在设备锁定时会要求认证，`Add / Update / Delete` 仍可能在用户没有看到 App 内完整证据时执行外部写入。Ailoha 的默认产品循环是“理解 → 提议 → 确认 → 执行”；高后果确认应打开 App，在完整上下文中重验 account、environment、task、widget、revision 和权限。

第一版建议：

- 允许 `Open review / Open task / Open Ailoha`；
- 允许“只关闭展示”的 dismiss，但文案必须与“取消服务器任务”区分；
- Calendar / Contact / Reminder / Invitation 的外部写入不直接在 Live Activity 完成；
- 任何旧 instance deep link 都不能结束或操作新 instance。

## 目标结构：一条 policy，不让 View 各自决定

这不是最终架构，只是实现合同的最小语义：

```swift
struct LiveActivityCandidate {
    let taskID: OpaqueTaskID
    let activityInstanceID: ActivityInstanceID
    let execution: Execution
    let attention: Attention
    let freshness: Freshness
    let eventRevision: Int64
    let taskKind: TaskKind
}

struct LiveActivityPublicProjection {
    let title: LocalizedStringResource
    let supportingText: LocalizedStringResource?
    let action: SafeSystemSurfaceAction?
    let accessibilityLabel: String
}

func selectSpotlight(from candidates: [LiveActivityCandidate]) -> SelectionDecision
func projectPublicSurface(_ candidate: LiveActivityCandidate) -> LiveActivityPublicProjection
```

`selectSpotlight` 只决定谁能占据系统表面；`projectPublicSurface` 只决定能公开什么。App 内完整模型、Activity payload 和 Widget View 不得互相代替。接受产品选择后，再用 Formal Architecture 固定 producer、registry、queue、revision、redaction、deep link 和 recovery。

## 十张截图与两条视频，怎么证明不是纸上政策

| ID | 必须拍到 | 失败长什么样 |
| --- | --- | --- |
| `POL-LA-01` | 两个同优先级任务进入；系统始终只有一张 task-bound 卡，第二项留在 App 队列 | 两张卡并存或新任务静默替换旧 task |
| `POL-LA-02` | `observe` 被 `resolve` 显式抢占；旧卡 end receipt 与新 instance 可对账 | 旧 deep link 仍能操作新卡 |
| `POL-LA-03` | 队列为空；系统没有常驻 idle 卡 | `0 Tasks Waiting` 长期占据锁屏 |
| `POL-LA-04` | Calendar pending/review 的 compact、expanded、Lock Screen 都是无害摘要 | 标题、精确时间或地点外泄 |
| `POL-LA-05` | Contact / To Meet 的 Lock Screen 与 VoiceOver 无身份和 notes | 姓名、公司、电话、邮箱或 note 出现 |
| `POL-LA-06` | Invitation 明确 `Nothing sent yet`，主动作只打开 App review | 绿色成功态或直接 Send/Add |
| `POL-LA-07` | privacy redaction 开启时仍有可理解的自定义摘要 | 整卡空白、系统占位符代替语义 |
| `POL-LA-08` | 旧 task / instance deep link 安全 fallback，不结束新卡 | 旧链接命中当前任务或跨账号打开 |
| `POL-LA-09` | logout / account / environment switch 后卡片与 route 都失效 | B 账号看到 A 的上下文 |
| `POL-LA-10` | payload、日志、telemetry、截图 manifest 与 VoiceOver allowlist 都为零 `D2/D3` | 只把视觉隐藏，数据仍进入 payload/log |

两条未剪辑视频：

1. `POL-VID-01-card-selection.mov`：同优先级排队 → resolve 显式抢占 observe → 旧 link fail closed → 队列清空无卡。
2. `POL-VID-02-disclosure.mov`：Calendar → Contact → Invitation 的 compact / expanded / Lock Screen / VoiceOver；全程无 PII，Invitation 进入 App 才显示完整证据并确认。

共同条件：同一 build revision、设备、OS、账号/环境、locale、appearance、Reduce Motion；每张图记录 task/instance hash、selection reason、disclosure class、capture source 和 Reviewer。

## 自动与人工验收

### 自动合同

- `selectSpotlight`：0/1/2/10 个 candidate、相同优先级、注意力升级、terminal、stale、logout、duplicate 与 out-of-order；任意时刻 active 数量 ≤1。
- token / registry：当前 active instance 的 token 与 task/instance 完全一致；旧 generation 无写权限。
- disclosure projector：所有 `TaskType × Execution × Attention × Freshness × Surface` 都只产生 allowlist 字段；未知 task 默认 D0。
- payload/log/VoiceOver：静态与 runtime 测试搜索 D2/D3 fixture；任何命中失败。
- direct action：外部写动作在系统表面不可达；deep link 回 App 后重验身份和 revision。
- Release：Debug fixture、测试 PII、截图 helper 和旁路 action 不进入 Release 产物。

### 人工判断

- 8 名未参与设计的人看到 Lock Screen 两秒后，至少 7 人说对“正在做什么 / 是否要回来”，0 人能识别具体联系人、公司、地点或任务原文。
- 8 人中 0 人把 `Invitation draft` 理解成“已经发出”。
- 双任务走查中，8 人里至少 7 人能说出当前卡跟的是哪件事，新任务为什么没有悄悄替换它。

## 与 Talent Signal 的关系

Talent Signal Gate 1 可以验证 D0 文案、阶段/注意力是否读得懂，以及“同一任务只有一个 Activity instance”的实现手感。它不能替 Ailoha 决定：

- Ailoha 是 Spotlight 单卡还是多卡；
- Ailoha 各 task type 的锁屏披露；
- Ailoha 是否允许系统表面直接写 Calendar / Contact；
- Ailoha 的多账号、Widget Intent、ARPC 与 APNs 生产恢复。

因此这两项产品决定可以与 Talent Signal 8 人冷读并行；不用等待 `selectedDirection` 才开始选择。

## 现在需要产品 owner 在画布上做的三个选择

1. **卡片政策：** A 多卡 / B task-bound Spotlight / C 永久 Session。推荐 B。
2. **披露政策：** 第一版严格 D0 / 默认 D0 + 用户 opt-in D1 / 维持详细展示。推荐严格 D0，opt-in 以后单独实验。
3. **动作政策：** 系统表面只 Open / 允许低风险 dismiss / 继续允许直接 Add-Update-Delete。推荐 Open + 明确的展示 dismiss，高后果写入回 App。

在这三项被接受前，本页状态保持 `proposed`；不得把推荐候选写成已确认 target architecture。
