Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions HANDOFF.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ make macos-offline-tests

## 9. 会话日志(最近)

- 2026-11-12 / Claude Code(air):**首键 +4.6 MiB 归因完成(报告 265 §4.2),不改产品代码**。读 `vmmap` 的 MALLOC ZONE 存活分配:首键时点只有两种状态——5.2 MiB(无会话存活,数据已随 `Weak` 缓存释放)或约 19 MiB(有会话存活);新包 8 轮里 3 轮撞上后者、旧包 5 轮 0 轮(偶然概率约 20%),无会话的轮次新旧完全相同。稳态存活堆新包 +0.5 MiB(21.0 vs 20.5)。`memory-gate.sh` 新增参考字段 `heap_live_mb` / `heap_allocations`。副产结论:冷首键 ~30 ms 很可能来自「最后一个会话释放后重新读盘解析」,与本批无关,是否常驻数据需 ADR。PR #273 待合并;已装仍是 `3e428b7`。
- 2026-11-12 / Claude Code(air):**内存新旧包 A/B(报告 265 §4.1)**。新→旧(1.2.1 发布版)→新,各经 `install.sh` 覆盖安装。稳态私有中位数:`six-words` 新 31.8 / 旧 33.0,`idle-8s` 新 31.6 / 旧 31.0 → **稳态无回归**;但学习区开启时两者都贴 34 MiB 门槛(旧包最高 33.7,新包 11 个样本 2 个超限,同包三次运行 FAIL/PASS/FAIL)。**新发现:首键时点新包 +4.6 MiB(27.8 vs 23.2),未归因。** 结束状态:已装新包 `f38e4e1a…`(`3e428b7`)、输入源萤火虫、config 未变;`updates/` 多出两份 A/B 产生的备份(`…T105406Z`、`…T110001Z`)未删。待用户裁定:门槛是否重测调整、首键内存是否发版前归因。
- 2026-11-12 / Claude Code(air):**报告 265:延迟 PASS、内存门禁 FAIL(未定性)**。PR #272 已合并,`dev`=`main`,无开放 PR、无多余分支。补 `;` 选第二候选真机样本(PASS;`keytopid` 加命名键 `semicolon`),`12:30` 与五笔码浮层探针测不了。`perf-session`:稳态 p95 7.33 ms / 0 外联(2 轮失焦,汇总脚本崩溃,按原始数据重算)。`memory-gate`:学习区开、门槛 34 MiB,稳态私有 31.0–34.6 MiB,1/6 样本超限;当时 load 12–41、swap 10.2/11.3 GB。**下一步:对新旧包做同条件内存 A/B(旧包在 `updates/install-backup-20261112T060612Z/`),结论出来前不升版本号、不发版。** 已装仍是 `3e428b7`,输入源已选回萤火虫。
- 2026-11-12 / Claude Code(air):**P1-U 真机键入复验 22/22 PASS(报告 264 §3.1)**。用户为代理会话的责任进程(`~/Library/Application Support/Claude/claude-code/<版本>/…/claude.app`,不是 `/Applications/Claude.app`)授了辅助功能权限后复跑 `quality-samples.sh`:原 13 条 + ADR-325…336 的 9 条全过。跑后 `config.toml` 取值还原但多出两行显式默认值(SHA `88651507…`→`52af2b90…`);词库 1713→1715 行(含样本副作用 `pinyin zhuang 装 1`)。**未验证**:`;`/`12:30`、五笔码浮层、练习新题型、设置页新开关、DMG 完成页(需人工目视);内存门禁与延迟未复测。PR #272(报告与探针工具)待合并。
- 2026-11-12 / Claude Code(air):**P1-U 候选 `3e428b7` 装机 PASS、真机键入复验 BLOCKED(报告 264)**。PR #271 已合并;从 CI 验收包(run `43163118879`,ZIP `8e7aece7…`,Notarized)覆盖安装:切 ABC → 停宿主 → `install.sh`,装后执行 `f38e4e1a…` = 包内,config/词库/练习进度 SHA 不变,宿主跑新二进制。**键入复验一条都没跑成**:本会话进程 `AXIsProcessTrusted=false`,合成按键被系统丢弃。已给 `quality-samples.sh` 加 9 条 ADR-325…336 样本,并让 preflight 能识别「无事件投递权限」(`keytopid --check-access`)。**下一步:用户在已授权终端跑报告 264 §4 的一条命令,再按 §5 目视 5 类项**;通过后才谈升版本号与发版。
- 2026-11-12 / Claude Code(air):**P1-U 第三批(ADR-333…336)+ 前两档已合入 main**。PR [#269](https://github.com/EasyIndie/EasyInputMethod/pull/269)(第一档)与 [#270](https://github.com/EasyIndie/EasyInputMethod/pull/270)(第二档)均已用合并提交并入(`c882c06`、`2f8ee06`),**下面一条日志里「第二档未推送」已过时**。第三批:按应用记住中英状态(仅内存)、练习「词组」题型(随包码表前 800 词)、薄弱字清单(`practice.collect_weak_chars` 默认关,`weak-chars.tsv`)、小鹤双拼(`pinyin.scheme`,仅拼音模式;425 个音节 419 个可达)。Schema 27→30。四道检查全绿、评测 98.2%/86.4% 与基线一致。**三批改动全部未装机**,复验清单见 `docs/待办与积压.md` P1-U;P1-U 剩余 8 项都需用户先定口径或授权取数据(同处有表)。仓库已开启 auto-merge。
Expand Down
133 changes: 133 additions & 0 deletions docs/reviews/265-p1u-followup-latency-memory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# 265 · P1-U 候选 `3e428b7` 补充复验:分号选词、延迟、内存

- 日期:2026-11-12 / Claude Code(air 本机)
- 范围:报告 264 之后的收尾——仓库状态核对、把探针测不了的项尽量补上、延迟与内存门禁复测
- 结论:**延迟 PASS(稳态 p95 7.33 ms,0 外联);内存门禁在学习区开启时不稳定——新旧包 A/B 显示稳态无回归,但两者都贴着 34 MiB 门槛**(§4.1)。
新包「首键」时点高约 4.6 MiB 已归因(§4.2):**不是泄漏、也不是数据变多**,是采样时是否恰好有一个输入会话存活;稳态存活堆新包多约 0.5 MiB。

## 1. 仓库状态(核对时)

- 工作区干净;`dev` = `main` = `1265fa7`;开放 PR 0 个;远端只有 `dev` / `main`。
- 清理:本地 `main` 落后 273 个提交 → 快进到 `origin/main`;`/private/tmp/main-wt` 的失效工作树记录 → `git worktree prune`。无其它分支可清。

## 2. 补样本

| 项 | 结果 | 说明 |
|---|---|---|
| `;` 选第二候选(ADR-327) | **PASS**(真机) | `keytopid` 新增命名键 `semicolon`。`nihao` `;` `⏎` 实得「你好吗」——第二候选是用户词库里学过的补全词,选中的确实是第二候选而不是「你好;」。因第二候选随用户词库变化,样本只断言「不是首选、也不是首选+;」 |
| `12:30`(ADR-325) | **探针测不了** | 合成的 Shift+; 到宿主时仍是 `;`(实得 `12;30`),无法区分;仍只有 `ime-core` 单测覆盖,需人工打一次 |
| 五笔码浮层(ADR-331) | **可见性未证实** | 诊断日志显示拼音上屏后提示路径已执行且取到了有效光标矩形(TextEdit,`pick … rect=…`);但窗口列表里看不到宿主的在屏窗口,浮层又不在 AX 树里,**是否真的显示出来仍需目视** |

`quality-samples.sh` 现为 23 条(原 13 + ADR-325…336 的 10 条)。全量第三轮:原 22 条全部 PASS;本轮试加的 3 条里 2 条因上述原因撤掉、1 条改写后单独复跑 PASS。

## 3. 延迟(PASS)

`perf-session.sh`,三段各 10 轮;口径按 ADR-307(稳态不含每轮首键)。

| 段 | 有效轮 | 稳态键数 | p50 | **p95** | max | 冷首键 p95 |
|---|---|---|---|---|---|---|
| basic | 8 | 120 | 3.78 | **7.96** | 11.07 | 35.72 |
| long-code | 10 | 119 | 3.58 | **6.95** | 9.68 | 29.88 |
| paging | 10 | 90 | 2.92 | **5.69** | 6.50 | 36.48 |
| **合计** | 28 | 329 | — | **7.33**(门槛 16) | 11.07 | 35.72(不设门槛) |

- 网络:485 次采样,TCP/UDP 连接合计 **0**。
- 对比:1.2.0 候选稳态 p95 5.80 ms、冷首键 28.86 / 33.39 ms(报告 259)。稳态略高但远低于门槛;冷首键略高,按 ADR-307 只记录。
**本机当时负载很高(load average 12–41),数字偏悲观。**
- **工具缺陷(未修)**:`basic` 第 8、9 轮测试窗口没拿到前台(BLOCKED),脚本的汇总步骤读到空结果直接崩溃、没有给出判定;
上表是用 28 个有效轮的原始数据按同一口径重算的。应让汇总跳过 BLOCKED 轮并如实报告轮数。

## 4. 内存门禁(FAIL,未定性)

`memory-gate.sh` 3 轮;口径 = 稳态时点的「私有已提交」。用户配置开着学习区,按 ADR-312 门槛为 **34 MiB**。

| 轮 | first-key | six-words(稳态) | idle-8s(稳态) |
|---|---|---|---|
| 1 | 27.2 | **34.6** ✗ | 31.5 |
| 2 | 27.0 | 31.1 | 31.0 |
| 3 | 26.7 | 33.7 | 31.7 |

- 6 个稳态样本里 1 个超限(34.6 > 34.0),脚本判 FAIL。
- **不能据此断定是回归**:测量时 swap 已用 10.2 / 11.3 GB、load average 最高 41,而该口径把换出页也计入;
三轮之间 `six-words` 相差 3.5 MiB,噪声与超限幅度同量级。学习区开启时的历史参考是「比关闭多约 3 MiB」(报告 256),即约 31–32 MiB。
- **也不能排除是回归**:本批新增了拼音补全与分段候选(每键多产生最多几十个候选对象)、更多配置项与两个新窗口题型的代码。
- **定性方法(未做,需再换两次包)**:同一时段、同一配置下,对旧包(`updates/install-backup-20261112T060612Z/`,1.2.1 发布版)
与新包各跑一次 `memory-gate.sh ROUNDS=5`,比较稳态中位数;差值 > 1 MiB 才按回归处理。最好在重启后、负载正常时做。

### 4.1 新旧包 A/B(同机、同配置、同一时段)

顺序:新包 → 旧包(1.2.1 发布版 `3eeeb0aa…`,取自 `updates/install-backup-20261112T060612Z/`)→ 新包。测量期间 load average 3.7–24,swap 约 10.9 / 12 GB。
单位 MiB,「私有已提交」。

| 包 | 轮数 | first-key | six-words(稳态) | idle-8s(稳态) | 脚本判定 |
|---|---|---|---|---|---|
| 新 `f38e4e1a…`(第 1 次) | 3 | 25.4 / 27.2 / 30.7 | 33.2 / 31.8 / 31.5 | 33.1 / 31.6 / 31.3 | PASS |
| 旧 `3eeeb0aa…` | 5 | 23.2 / 25.5 / 23.7 / 23.2 / 22.6 | 33.5 / 33.7 / 31.0 / 32.1 / 33.0 | 31.5 / 31.0 / 30.8 / 30.3 / 31.0 | PASS |
| 新 `f38e4e1a…`(第 2 次) | 5 | 25.6 / 24.7 / 28.3 / 28.9 / 31.8 | 31.6 / **34.2** / 33.0 / 31.8 / 31.8 | 31.5 / 30.9 / 32.1 / 31.8 / 31.6 | **FAIL** |

| 时点 | 旧包中位数 | 新包中位数(8 轮合并) | 差 |
|---|---|---|---|
| six-words | 33.0 | 31.8 | **−1.2**(新包不高于旧包) |
| idle-8s | 31.0 | 31.6 | **+0.6**(低于预设的 1 MiB 回归阈值) |
| first-key(不在门禁口径内) | 23.2 | 27.8 | **+4.6** |

判读:

1. **稳态没有可判定的回归**:`six-words` 新包反而低,`idle-8s` 高 0.6 MiB,小于事先定的 1 MiB 阈值,也小于轮间抖动(约 3 MiB)。
2. **门禁本身在这个配置下贴线**:学习区开启时旧包稳态最高已到 33.7 MiB,离 34 只有 0.3;新包 11 个 `six-words` 样本里 2 个超限
(34.6、34.2),同一份包三次运行出现 FAIL / PASS / FAIL。门槛 34 是按「开学习区多约 3 MiB」定的(ADR-312),在本机当前系统状态下余量不够。
3. **新包首键时点高约 4.6 MiB**:这是真实差异(两次新包测量一致偏高,旧包 5 轮都低)。它不计入门禁,稳态会收敛到同一水平,
但说明首键路径上多分配了内存——最可能来自拼音补全/分段候选(首键即扫描词表前缀区间并构造候选),**未做归因**。

处置建议(需用户裁定,未自行改门槛):
- a) 认定「稳态无回归」,把学习区开启时的门槛问题单列——在重启后、swap 正常时各测 5 轮,再决定是否调 ADR-312 的 34 MiB;
- b) 另开一项归因首键 +4.6 MiB(看是否能让补全/分段候选惰性化),与发版解耦或作为发版前的小修,二选一。

A/B 过程中的换包:旧包与新包各经 `install.sh` 覆盖安装一次,`updates/` 下因此多出两份备份
(`install-backup-20261112T105406Z` 是新包、`…T110001Z` 是旧包),未删除。结束状态:已装新包 `f38e4e1a…`(`3e428b7`)、
宿主跑新二进制、输入源为萤火虫、`config.toml` 未变(`99-restore-ab.txt`)。

### 4.2 首键 +4.6 MiB 的归因

从 A/B 留下的 `vmmap` 快照里读 MALLOC ZONE 表的**存活分配**(字节与个数),而不是「私有已提交」:

| 包 | 时点 | 存活堆 MiB(逐轮) | 存活分配个数(逐轮,千) |
|---|---|---|---|
| 旧 | first-key | 5.2 / 5.2 / 5.2 / 5.2 / 5.2 | 26.7 ×5 |
| 新(两次共 8 轮) | first-key | 5.1 / 5.2 / **19.2** / 5.2 / 5.2 / 5.2 / **19.4** / **19.2** | 26.7 ×5,**188–191 ×3** |
| 旧 | six-words / idle-8s | 20.5–20.6 / 20.4–20.5 | 188–189 |
| 新 | six-words / idle-8s | 21.0–21.2 / 20.9–21.2 | 190–193 |

1. **首键时点的存活堆只有两种状态**:5.2 MiB(约 2.67 万个分配)或约 19 MiB(约 19 万个分配)。差的那 14 MiB 就是码表与拼音数据。
引擎数据由会话以 `Arc` 持有、缓存只留 `Weak`(`ime-ffi/src/data.rs`)——**没有任何会话存活时数据整体释放**。
所以这个时点测到的是「测试窗口退出后,是否恰好有别的客户端的输入会话已经激活」,而不是首键路径分配了多少。
2. 新包 8 轮里 3 轮处于「有会话存活」状态,旧包 5 轮里 0 轮;把这 3 轮按「私有已提交」算进中位数,就得到了 +4.6 MiB。
**在「无会话存活」的那 5 轮里,新包的存活堆与旧包完全相同**(5.2 MiB,分配个数相差不到 30)。
3. 这是采样时机问题:13 轮里共 3 轮「有会话」,全落在新包的概率按超几何分布约 20%,不足以说明是新代码造成的;
新增代码里也没有会延长控制器或会话生命周期的引用(闭包均为 `weak self`,`AppLanguageMemory` 只存字符串与布尔)。
**不能完全排除**新包让别的客户端更早激活,但没有找到机制。
4. **稳态存活堆新包多约 0.5 MiB**(21.0 vs 20.5,分配多约 1,500–4,000 个)。这是真实增长,量级与「每次组合多保留几十个补全/分段候选、
`Candidate` 多一个字段、更多配置项」相符,未进一步拆分。
5. 「私有已提交」在首键时点远大于存活堆(23–28 MiB 对 5.2 MiB):多出来的是分配器已释放但尚未归还系统的页。
该口径受采样时机与碎片影响大,这也是 §4.1 里同一份包三次判定不一致的原因之一。

**结论**:首键内存不需要改产品代码。**工具已改**:`memory-gate.sh` 的每个采样现在额外输出 `heap_live_mb` 与 `heap_allocations`
(只作参考、不参与判定),以后能直接区分「数据真变多了」与「分配器残留 / 会话是否存活」。

**对冷首键延迟的含义**:数据随最后一个会话释放,意味着「所有输入框都失焦后的第一次按键」要重新读盘解析码表——
这很可能就是冷首键 30 ms 量级延迟(ADR-307)的主要来源,且与本批改动无关。是否改成「宿主存活期间常驻数据」是内存换延迟的取舍,
需单独立 ADR(会让无会话时的常驻内存从约 5 MiB 升到约 20 MiB)。

## 5. 仍未验证

`12:30`、五笔码浮层是否可见、练习「字根」「词组」题型、设置页 7 个新开关、DMG 安装完成页——都需人工目视。

## 6. 跑完后的状态

输入源已切回萤火虫(内存门禁结束时停在 ABC,已手动选回);已装执行 SHA `f38e4e1a…` 未变;`config.toml` SHA `52af2b90…` 未变;
`weak-chars.tsv` 不存在;探针自开的 TextEdit 文档已关闭并退出。

## 7. 证据

`platforms/macos/build/acceptance-air-p1u-3e428b7/`:`22-quality.txt`、`quality-3/`、`30-perf.stderr`、`perf/`(逐轮 JSON 与 `net.csv`)、
`31-memory.txt`、`memory/`、`40-mem-new1.txt`、`41-mem-old.txt`、`42-mem-new2.txt`、`memory-ab-*/`、`99-restore-ab.txt`。
1 change: 1 addition & 0 deletions docs/reviews/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
| 1.1.0 发布线 | 225–250 | 简码/排序、验收成本、延迟归因、1.1.0 发布与站点 |
| 1.2.0 候选与发布 | 251–262 | B1 门禁、B0 运行时、学习区、练习四题型与键位图、1.2.0 发布 |
| 1.2.1 清理 | 263 | 安装/无障碍补丁、死代码与文档审计 |
| P1-U 补充复验 | 265 | 分号选词真机 PASS;延迟稳态 p95 7.33 ms PASS;内存 A/B:稳态无回归但学习区开启时贴 34 MiB 门槛;首键 +4.6 MiB 归因为采样时会话是否存活(非泄漏),稳态存活堆 +0.5 MiB |
| P1-U 装机 | 264 | ADR-324…336 候选 `3e428b7` 装机 PASS;真机键入复验 22/22 PASS(首轮因无辅助功能权限 BLOCKED,授权后复跑);5 类目视项待人工 |

## 4. 常见查找
Expand Down
4 changes: 2 additions & 2 deletions docs/tasks/T-ITER-22-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

| IT-22 条目 | 覆盖手段 | 怎么跑 |
|---|---|---|
| 1.1–1.3、1.7–1.12 上屏/标点/全角/简繁/模式/中英 | 热键探针 + 质量样本 | `make macos-auto-acceptance`(7 项)、`tools/macos-probe/quality-samples.sh`(22 项) |
| 1.1–1.3、1.7–1.12 上屏/标点/全角/简繁/模式/中英 | 热键探针 + 质量样本 | `make macos-auto-acceptance`(7 项)、`tools/macos-probe/quality-samples.sh`(23 项) |
| 1.4 翻页、1.5 方向键选择 | 组合导航单测 + 质量样本「翻页键不破坏输入」 | `make test`、`quality-samples.sh` |
| 2.1 长组合不丢字 | 质量样本「长文本不丢字」 | `quality-samples.sh` |
| 2.2–2.6 退格/Esc/Home/End/Delete/Tab | 核心与 FFI 单测(ADR-164) | `make test`(`ime-core`/`ime-ffi`) |
Expand Down Expand Up @@ -77,7 +77,7 @@ make macos-acceptance-full # ~5 分钟:preflight → 热键/上屏 →
## 4. 与旧清单的差异(为什么能砍掉 47 项)

0. **只依赖系统自带应用**(报告234):TextEdit / Safari / Terminal 之外的一律 optional(装了才测、没装 SKIP),由 `test-acceptance-portability.py` 强制。
1. **同一件事此前被写了三遍**:`quality-samples.sh`(16 项)、`acceptance-auto.sh`(8 项)、IT-22 手工表(12 项重叠)。报告 230 之后两份脚本**按行为分工**:`quality-samples.sh` 断言打字产物(22 项;报告264 起含 ADR-325…336 的 9 条:数字后标点、拼音补全、以词定字、分段选词、双拼、薄弱字、按应用记住英文),`acceptance-auto.sh` 断言热键状态语义(7 项),重复项已删并由 `test-acceptance-map.py` 守住不再回流。
1. **同一件事此前被写了三遍**:`quality-samples.sh`(16 项)、`acceptance-auto.sh`(8 项)、IT-22 手工表(12 项重叠)。报告 230 之后两份脚本**按行为分工**:`quality-samples.sh` 断言打字产物(23 项;报告264/265 起含 ADR-325…336 的 10 条:数字后标点、拼音补全、分号选词、以词定字、分段选词、双拼、薄弱字、按应用记住英文),`acceptance-auto.sh` 断言热键状态语义(7 项),重复项已删并由 `test-acceptance-map.py` 守住不再回流。
2. **离屏单测已经断言了 GUI 结构**:候选/设置/关于/学习的布局与文案由 `test-*-presentation.py` 断言,手工"看一眼分组是否连续"不再重复。
3. **几何/落点类**(多屏、边缘、翻页位置)已有 `CaretPlacement` 与离屏快照对比度自动判定(ADR-301、报告 197)。
4. **安装类**从"每次走查"降为"每版一次",因为它们只在换包时才有意义。
Expand Down
Loading
Loading