Files
web-terminal/docs/plans/ios-completion.md

150 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ios-completion — 收尾波6 项整改 + P2 全波 + Android 令牌对齐
> 编排 doc。**orchestrator 冻结的跨 agent 契约在 §1任何 builder 不得偏离**
> 任务/Owns 表在 §2验收在 §3。进度记录仍归 `docs/PROGRESS_LOG.md`orchestrator 独写)。
审计依据2026-07-29 的 16-agent iOS 完成度审计43 计划任务 27 DONE / 11 PARTIAL / 5 MISSING
用户已定的三个范围问题:
1. **P2 全 5 项T-iOS-31…35在范围内** —— 计划 §7 要求"未按 P0 规格扩写 RED 清单前不得分派"
故每个 P2 builder 的**第一步就是扩写自己那一项的 RED 测试清单**(写进本 doc §4 追加区),再进入 GREEN。
2. **Apple 账号 = 免费个人 team**Team ID `C738Z66SRW`O=`Yaojia Wang`)。
**Push Notifications capability 不可用**`aps-environment` entitlement 若默认挂上,真机构建会直接失败。
entitlements 必须做成**默认不挂、env 开关**(见 A1
3. **Android 的 `WEBTERM_TOKEN` 支持一起补**(与 iOS 侧文件完全不相交,可并行)。
---
## 1. 冻结契约FROZEN — orchestrator 已核对服务端源码builder 只许实现不许改)
### 1.1 访问令牌(`WEBTERM_TOKEN`)—— 原生客户端接入方式
服务端事实(`src/http/auth.ts``src/server.ts:340-430,1353-1385`
| 项 | 值 | 出处 |
|---|---|---|
| Cookie 名 | **`webterm_auth`** | `auth.ts:30` `AUTH_COOKIE_NAME` |
| Cookie TTL | 2592000 秒30 天) | `auth.ts:34` |
| 登录端点 | **`POST /auth`** | `server.ts:393` |
| 请求体 | `{"token":"<t>"}``Content-Type: application/json` | `server.ts:396,408-409` |
| **`Accept` 头** | **必须不含 `text/html`** | `server.ts:366-369,410` —— 含 `text/html` 会被当成表单走 302 重定向,而不是 204/401 |
| 成功 | **204**,带 `Set-Cookie: webterm_auth=…` | `server.ts:412-414` |
| 令牌错误 | **401** + `{"error":"invalid token"}` | `server.ts:418` |
| 限流 | **429**10 次/分钟/IP | `server.ts:100,399-402` |
| **服务端未启用鉴权** | **204 但没有 `Set-Cookie`** | `server.ts:404-407` |
| 鉴权后 | 每个 HTTP 请求 + **WS upgrade** 都要带 `Cookie: webterm_auth=<t>` | `server.ts:1375` |
| 令牌字符集 | `[A-Za-z0-9._~+/=-]`,长度 16512 | CLAUDE.md / config 校验 |
**实现决定(冻结)**
- 原生客户端**自己知道令牌**,因此 **直接手写 `Cookie: webterm_auth=<t>` 请求头**即可,
**不解析 `Set-Cookie`**、不依赖系统 cookie 存储。理由URLSession/OkHttp 的 cookie jar 在
WS upgrade 上的行为不一致且难测;手写头与既有 `Origin` 手写头是同一个模式(`Endpoints.swift:81`
可被纯函数单测钉死。
- `POST /auth` 只用作**配对期的一次性校验探针**
- 204 **有** `Set-Cookie` ⇒ 令牌正确,保存;
- 204 **无** `Set-Cookie` ⇒ 该服务器**没开鉴权**,令牌不必保存(**不得**据此认为"已认证"
- 401 ⇒ 令牌错UI 报"令牌不正确"429 ⇒ UI 报"尝试过多,稍后再试"。
- **`Cookie` 头与 `Origin` 头正交**:令牌**不替代** Origin 检查,两者都要带(`server.ts:1363-1379` 是先 Origin 再 cookie
- **令牌是密级材料**:存 Keychain / Android Keystore-backed 存储,
`kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`、**禁 `kSecAttrSynchronizable`**(沿用 `SecItemShim.swift` 既有约定);
**绝不写日志、绝不进 URL query、绝不进崩溃报告**
- **401 语义**:任何 RO/G 请求收到 401 ⇒ 抛类型化 `.unauthorized`(不是通用网络错),
UI 引导去补令牌。WS upgrade 收 401 ⇒ **终态,不进退避环**(与 `.replayTooLarge` 同级处理)。
### 1.2 git 面板端点(照 Android 已实现的形状;服务端为唯一真源)
Origin-iff-G 规则(`Endpoints.swift:81`)照旧:**写操作必带 `Origin`,只读绝不带**。
| 端点 | 方法 | R/W | Android 参考实现 |
|---|---|---|---|
| `/projects/log` | GET | RO | `api-client/.../models/GitLog.kt` |
| `/projects/pr` | GET | RO | 同上 |
| `/projects/worktree/state` | GET | RO | — |
| `/projects/git/stage` | POST | **G** | `models/GitWrite.kt` |
| `/projects/git/commit` | POST | **G** | 同上 |
| `/projects/git/push` | POST | **G** | 同上 |
| `/projects/git/fetch` | POST | **G** | — |
| `/projects/worktree` | POST | **G** | `routes/GitRouteShapeTest.kt` |
| `/projects/worktree/prune` | POST | **G** | 同上 |
| `/sessions` | GET | RO | —(`claude --resume` 历史T-iOS-32 |
| `/live-sessions/:id/queue` | POST | **G** | —w2 pty 注入队列) |
**真源是 `src/` 的实现**`src/http/projects.ts` / `src/server.ts`Android 只作交叉校验;
两者若不一致,**以 `src/` 为准并在返回条目里报告该不一致**。
### 1.3 免费 team 的签名与 entitlements冻结
- `DEVELOPMENT_TEAM = C738Z66SRW`
- `CODE_SIGN_IDENTITY` 的历史值 `"iPhone Developer"` 是**已废弃**串,改 `"Apple Development"`(或删掉让 Automatic 自己选)。
- `WebTerm.entitlements`(含 `aps-environment`**默认不挂**。挂载由 **env 开关**控制,
未设时 `CODE_SIGN_ENTITLEMENTS` 必须为空 —— 免费 team 挂上就真机构建失败。
开关名冻结为 **`WEBTERM_PUSH_ENTITLEMENTS`**(值=entitlements 相对路径),并写进 `ios/README.md`
- `UIBackgroundModes: [remote-notification]` 可以无条件加(背景模式不是 capability免费 team 不受限)。
---
## 2. 任务表Owns 铁律:不得编辑本任务 Owns 之外的文件)
### Wave A串行1 agent—— 全员依赖的工程根
| ID | 任务 | Owns |
|---|---|---|
| **A1** | 签名解锁 + Info.plist 全量键 + 可选 entitlements | `ios/project.yml``ios/App/WebTerm/WebTerm.entitlements`(新) |
A1 必须一次把**后续所有波次需要的 Info.plist 键**都加好(它是 project.yml 的唯一 owner
`NSMicrophoneUsageDescription``NSSpeechRecognitionUsageDescription`T-iOS-31
`UIBackgroundModes: [remote-notification]`T-iOS-21
### Wave B并行 5 agent目录互斥—— 包层 + CI + Android
| ID | 任务 | Owns |
|---|---|---|
| **B1** | APIClient 全部新端点§1.1 `/auth` + §1.2 全表)+ 模型 + 测试覆盖率≥80% | `ios/Packages/APIClient/**` |
| **B2** | HostRegistry按主机存访问令牌Keychain§1.1 存储约定)+ 迁移安全 | `ios/Packages/HostRegistry/**` |
| **B3** | SessionCoreWS upgrade 带 `Cookie`、401→终态 `.unauthorized`、不进退避环 | `ios/Packages/SessionCore/**` |
| **B4** | ClientTLS 覆盖率 55.76%→≥80% + 纳入覆盖率门 + 修 CI 三条腿 | `ios/Packages/ClientTLS/Tests/**``ios/IntegrationTests/scripts/coverage-gate.sh``.github/workflows/ios.yml` |
| **B5** | Android `WEBTERM_TOKEN` 支持§1.1 同一契约OkHttp 侧) | `android/**` |
### Wave C**串行** 4 agent—— App 层
串行原因:新增 `.swift` 文件必须 `xcodegen generate` 重写**共享的** `.xcodeproj`,并行会互相踩;
串行后无文件归属冲突,每个 agent 可自由跑 `xcodegen + xcodebuild` 全量验证。
| ID | 任务 | 内容 |
|---|---|---|
| **C1** | 令牌 UI + 传输接线;移除主机 UI顺带修 `PushRegistrar.handleHostRemoved` 死钩子) |
| **C2** | git 面板 UI + worktree 表(**含 T-iOS-32**+ `claude --resume` 历史 |
| **C3** | 终端内搜索(**T-iOS-33**+ 语音 PTT**T-iOS-31** |
| **C4** | 主题 + Dynamic Type**T-iOS-34**+ web `?join=` 互通(**T-iOS-35** |
### Wave D并行 3 agentreport-only / 文档)
| ID | 任务 |
|---|---|
| **D1** | 安全复核:令牌路径(两端)+ entitlements + 无令牌泄漏(日志/URL/崩溃) |
| **D2** | 全量验收(**T-iOS-36** + T-iOS-18/19/30 的机器可执行部分):包测/App 测/集成/覆盖率门/模拟器构建/真机构建尝试,只报真实数字 |
| **D3** | 文档:`README.md``ios/README.md``docs/PLAN_IOS_CLIENT.md``docs/PLAN_IOS_IPAD.md` 勾选与过期表述 |
### Wave E串行条件触发
| ID | 任务 |
|---|---|
| **E1** | 修 D1/D2 报出的 CRITICAL/HIGH无则跳过 |
---
## 3. 验收门(每个 builder 自查D2 复核)
- TDD先 RED 再 GREEN§4 的 RED 清单是 P2 任务的前置交付物)。
- `swift build` + `swift test` 本包全绿;改到 App 层则 `xcodegen generate` + iPhone 16 Pro 模拟器 `xcodebuild ... test` 全绿。
- 受门包覆盖率 ≥80%B4 后 ClientTLS 也进门)。
-`TODO`/`FIXME`/占位实现;零硬编码密钥;令牌零日志。
- **诚实报告**:跑不了的(真机/付费账号/CI 平台)写 DEFERRED 并给出手工步骤,**不得报"通过"**。
---
## 4. P2 RED 测试清单(由各 P2 builder 在开工第一步追加到本节)
<!-- C3 / C4 在此追加 T-iOS-31/33/34/35 的 RED 清单 -->