fix(ios,android): close the acceptance gaps the review found, add Android CI

- T-iOS-34's stated acceptance is "最大字号不破版" and it was failing: the key bar
  froze its height at 52pt while an AX5 keycap needs 108.24pt, so caps clipped —
  recorded as a withKnownIssue rather than fixed. Height now derives from the
  content size category (and tracks live changes via registerForTraitChanges);
  the known-issue marker is gone, replaced by positive assertions including a
  12-category no-clip sweep and a guard that stays red if anyone writes the
  constant back. Honest tradeoff: the keycap font is clamped at .accessibility2,
  the same policy the design system already applies to dense content, because an
  unclamped AX5 bar would eat the terminal. A test pins the clamp so the two
  cannot drift.

- Thumbnails silently 401'd on a token-gated host: the pipeline built its own
  transport with no token source, and by design never throws, so every preview
  degraded to a placeholder with no signal. Assembled from AppEnvironment now.

- Android had zero CI while the token wave shipped 24 files of secret-handling
  code. The instrumented leg is workflow_dispatch-only and says why in the file:
  no one has ever seen it green on a runner, and a required leg nobody trusts
  just produces a false green.

- Android persisted a validated token before the host probe succeeded, stranding
  a secret for a host that never paired.

App bundle 534 -> 550 on both simulators, zero known issues. Android 687 -> 691.
This commit is contained in:
Yaojia Wang
2026-07-30 16:46:20 +02:00
parent 284cfd193a
commit 5cc755b0b6
14 changed files with 1120 additions and 102 deletions

View File

@@ -24,6 +24,35 @@
> 新会话读到的第一块。保持准确,只描述"此刻"。
### 📱 [x] iOS 收尾波 — 6 项整改 + P2 全波 + 两端令牌(2026-07-30,worktree `ios-completion`)
- **起因**: 先做了一次 16-agent 的 iOS 完成度审计(2026-07-29),结论是"代码层面基本完工、交付层面卡在真机门口":43 个计划任务 27 DONE / 11 PARTIAL / 5 MISSING,**从未在任何真机上跑过一次**,且已落后服务端两个月的新功能。用户要求"全部实现",并定了三个范围问题:P2 全 5 项在内、Apple 账号是**免费个人 team**、Android 令牌一起补。
- **编排**: 契约先冻结在 [`docs/plans/ios-completion.md`](./plans/ios-completion.md) §1(orchestrator 亲自核服务端源码),再派 3 个 Workflow 共 22 个 agent。**A 串行 → B 并行 5 → C 串行 4 → D 并行 3 → E 条件 → 收尾波 4**。C 波刻意串行:新增 `.swift` 必须 `xcodegen generate` 重写**共享的** `.xcodeproj`,并行必互踩。
- 中途整个 C+D 波(7 个 agent)被 provider 侧 `529 Overloaded`/`ECONNRESET` 打挂;`resumeFromRunId` 把 A/B 从缓存回放、只重跑失败的,零返工。
- **冻结契约(§1.1,踩过的坑都在里面)**: cookie 名 `webterm_auth`;`POST /auth``{"token":"…"}`;**`Accept` 头不能含 `text/html`**——含了会被当表单走 302 而不是 204/401;**204 但无 `Set-Cookie` = 服务端根本没开鉴权**,绝不能据此认为"已认证";原生客户端**自己手写 `Cookie` 头**,不解析 `Set-Cookie`、不用系统 cookie jar(URLSession/OkHttp 的 jar 在 WS upgrade 上行为不一致且难测);**令牌不替代 Origin**,两者都要带。
- **真机构建解锁(最高杠杆,一次关掉 7 项验收阻塞)**: `DEVELOPMENT_TEAM=C738Z66SRW` + **target 级** `CODE_SIGN_IDENTITY`(废弃串 `"iPhone Developer"` 是 XcodeGen 的 product-type preset 在 target 层注入的,project 级怎么写都盖不住)→ 免费个人 team 上 `** BUILD SUCCEEDED **`,`-allowProvisioningUpdates` 现场签发 7 天 profile。**`aps-environment` 做成 env 开关且默认不挂**(`WEBTERM_PUSH_ENTITLEMENTS`),并**反向验证过必要性**:打开后真机构建报 `Personal development teams … do not support the Push Notifications capability`
- **顺手挖出一个会卡死全波的坑**: SwiftTerm 用浮动版本 `from: 1.13.0`,而 `.xcodeproj` 是 gitignore 的 → **任何人一 `xcodegen generate` 就解析到 1.15.0**,它在 1.14.0 新增的 `public var hasActiveSelection``TerminalScreen.swift` 本地同名属性冲突,且**加 `override` 修不了**(上游是 `public` 不是 `open`)。A1 先钉死版本止血,C3 删掉本地属性改用上游的、再抬到 1.15.0 根治。
- **交付**(全部实测,非引用):
| | 之前 | 之后 |
|---|---|---|
| 包测试 | 310 | **452** |
| App 测试 | 296(1 失败) | **550**(iPhone + iPad 各一遍,**0 known issue**) |
| 集成测试 | 10 | **26** |
| Android 测试 | 687 | **691** |
| ClientTLS 覆盖率 | 55.76%(**不在门内**) | **89.49%**(已入门) |
| 覆盖率门 | 4 个包 | **5 个包全过**(最低余量 +9.49pp) |
| 真机构建 | `BUILD FAILED` | **`BUILD SUCCEEDED`** |
- **令牌**: 两端全链路(APIClient `/auth` 探针 + Keychain/Keystore 存储 + WS upgrade 带 Cookie + 401 终态不进退避环)。
- **git 面板 parity**: iOS 补齐 `/projects/{log,pr,git/*,worktree*}` + `/sessions` + 队列——这是与 Android 和 web 差了两个月的那一块。
- **P2 全 5 项**: 语音 PTT(epoch 防误发)、终端内搜索、worktree 生命周期、主题 + Dynamic Type、web `?join=` 互通。
- **CI**: 修好 iOS 三条腿(缺 `npm ci` 会**硬失败**不是 skip / 缺 iPad UI 腿 / iOS 17 缺 runtime 时**静默报绿**),新建 `android.yml`(此前 Android 零 CI)。
- **复核抓到并已修的 2 个 HIGH**: ① iOS 的 WS 令牌是**与主机无关**地解析的,混合机群(一台开令牌一台没开)下令牌门主机永远开不了终端,且 App 内无解 → 改为每主机一个 transport;② Android 把主机自身的 git 凭据 401(`src/http/git-ops.ts:108`)误报成"你的访问令牌错了",因为通用 401 映射跑在路由映射前面 → git 写路由标 `ROUTE_DEFINED`,保住服务端原文案。
- **orchestrator 的两个裁定**: ① 令牌策略在 iOS 有 3 份、Android 1 份,**不解冻 `WireProtocol` 去收敛**(它是冻结的跨语言契约,共享密钥的 helper 不该进去),改为在 `IntegrationTests` 加漂移守卫——它是全树唯一能同时依赖三个包的目标;② `PairingProbe.swift` 越界改动是我特批的(B1 已收工释放该包),复核把它记为 LOW,已知悉。
- **诚实的未闭合项**: release ipa 层核对(免费 team 无分发通道);真机人工腿(口述 / 扫码 / 锁屏 Face ID / IME);APNs 端到端(需付费账号);**两个 workflow 从未在任何 runner 上跑过**——本仓库唯一 remote 是自建 Gitea,GH Actions 从来没执行过,本地已逐条复跑其命令;Android instrumented 腿(令牌静态加密的唯一证明)设为 `workflow_dispatch` 触发,理由写在文件头:没人见它绿过的腿若设为必过,只会逼出一个假绿。
- **一个诚实的取舍**: 键栏在 AX3AX5 的**字号**封顶在 `.accessibility2`。T-iOS-34 的验收原文是"最大字号不破版",现在任何档都不裁切;但"AX5 键帽以 AX5 字号渲染"是假的——不封顶时 AX5 需 108.24pt,会把终端吃掉。封顶值由测试钉死不得漂移。
### 🌿 [x] w6 项目详情 Git 面板 — G1G7 全部完成(2026-07-29,worktree `project-detail-git-mockup`)
- **动机**: 详情页只有一个光秃秃的 `●`,回答不了"有没有 commit 没 push / 现在在哪个 worktree"。设计稿 `docs/mockups/project-detail-git.html`,计划 `docs/plans/w6-project-git-panel.md`