docs: 收口阶段 0B 输入消费结论

This commit is contained in:
Codex
2026-10-07 04:09:36 +08:00
parent 0db94a7d41
commit 18caf91633
6 changed files with 915 additions and 10 deletions
@@ -0,0 +1,74 @@
# Fix-BUG-20261006-0002
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
## 关联信息
- Fix ID: Fix-BUG-20261006-0002
- 关联 BUG ID: BUG-20261006-0002
- 修复目标: 让阶段 0B 的真实 IME 实验具备可比较的位置基线、可命中的原生键规则和不因状态锁竞争主动丢记录的采集路径
- 状态:验证完成
- 负责人: Codex
- 分支 / 版本: `master`,未提交工作区
## 根因分析
- 根因 1: 0B 复用了只显示说明文字的 UiFixture 场景,没有把 0C 已验证的 caret point/line rect 写入当前 HWND 的 IME context。
- 根因 2: 规则按操作者语义配置为 `VK_ESCAPE`,但 IME 在 WndProc 前已处理组合态 KeyDown,窗口实际收到 `VK_PROCESSKEY`;scan code 1 才保留了本轮 Escape 的物理区分信息。
- 根因 3: 单环形缓冲在 safe-point 持锁复制,WndProc 为避免等待使用 `TryAcquireSRWLockExclusive`;宿主复制期间到达的原生消息会被主动丢弃。
## 修复方案
- 修复思路: 保持 0B 动作枚举只有 Forward/Consume 两值,仅修正诊断输入、位置基线和采集完整性,不新增生产路由行为。
- 关键改动:
- 0B 人工 runner 固定窗口为 `720×420`,传入客户区 caret `(225,282)` 与 line rect `(200,255)-(680,284)`。
- UiFixture 绘制与上述数值一致的 line region 和 caret marker,便于操作者判断候选 UI 是否仍落在窗口外。
- 在 `WM_SETFOCUS`、`WM_IME_SETCONTEXT`、`WM_IME_STARTCOMPOSITION`、`WM_IME_COMPOSITION` 观察点准备定位请求;锁外调用 `CFS_POINT` composition setter 与 `CFS_EXCLUDE` candidate setter,通过独立 `ime_position` 记录保存 context、两个 API 结果和坐标。
- Composition 请求 Consume 时,如果尚无一次两个 setter 同时成功的证据,立即 fail-open 为 `ime_position_unavailable`。
- 规则新增可选 `scanCode`。自动 F24 仍使用 any;人工 Escape 默认使用 `WM_KEYDOWN + VK_PROCESSKEY(229) + scanCode 1 + ComposingOnly`,界面指令仍要求操作者按 Escape。
- 分析器在指定 scan code 时同时过滤 WndProc 与 EasyX `EX_KEY`,并输出位置尝试/成功计数和 `imePositionReady`。
- 记录缓冲改为两个固定数组;safe-point 在锁内只交换 active buffer 和重置索引,文件写入在锁外完成。WndProc 使用极短阻塞临界区,不再因 `try-lock` 失败丢弃记录。
- 涉及文件 / 类 / 函数:
- `src/StellarX/SxInputProbe.cpp`
- `tests/UiFixture.cpp`
- `scripts/run-input-consume-probe.ps1`
- `scripts/run-input-consume-probe-manual.ps1`
- `scripts/analyze-input-probe.ps1`
- 影响的 API / 行为: 仅 `STELLARX_ENABLE_INPUT_PROBE` 且显式开启 0B 的诊断路径;公共 API 与默认生产窗口行为不变。
- 回滚点 / 开关: 不设置 `STELLARX_INPUT_PROBE_0B` 时不装载规则、不调用固定位置逻辑;默认构建不包含探测实现。
## 影响评估
- 正确性: scan code 消除同一 `VK_PROCESSKEY` 下字母、方向、Escape 等多类组合键的歧义;固定位置成功门槛防止把夹具定位失败算成消费副作用。
- 并发: 双缓冲消除宿主批量复制对 WndProc 的长时间占锁;文件写锁仍串行化多个 flush,状态锁序保持“文件写锁 → 状态锁”。
- 性能: WndProc 每条被观察消息增加一次短阻塞状态锁;Composition 生命周期增加有限的 IMM32 setter。两者只存在于诊断构建。
- 风险:
- 某些 IME 即使 setter 返回成功也可能自行调整候选 UI,因此仍需人工视觉确认。
- WndProc 阻塞临界区要求 safe-point 严禁在状态锁内做文件 I/O 或整批复制;后续维护若破坏此不变量可能重新引入卡顿。
- 首次人工复验发现持状态锁调用 IMM32 setter 会因同步重入 WndProc 自锁;已拆分为“锁内请求 → 锁外调用 → 锁内结果”并增加线程内递归定位保护,详见 `Fix-BUG-20261006-0003`。
- `VK_PROCESSKEY + scanCode 1` 是当前环境观察到的原生 Escape 形态,不得未经新证据外推到全部 IME/键盘布局。
## 验证结果
- 已完成:
- Debug x64 探测构建完成编译与链接;仅有 EasyX 头文件既有 C4201 警告。
- 失败截图已复制到对应忽略运行目录并记录 SHA-256。
- 默认 Debug x64 单元测试 15/15 通过。
- Windows PowerShell 5.1.22000.2538 与 PowerShell 7.6.5 对三份 0B 脚本均为 0 个 AST 解析错误。
- `git diff --check` 通过,仅输出仓库换行规范提示。
- 新分析器重放既有有效 F24 KeyDown A/B:Forward 仍为 `valid/present_once`,Consume 仍为 `valid/absent`,兼容未携带 `targetScanCode` 的既有 schema v2 日志。
- `BUG-20261006-0003` 修复后的微软拼音 Forward 人工复验为 `valid`:Escape 原生规则唯一命中并转发,18/18 次定位成功、零孤立结果、零丢失,人工截图确认候选 UI 位于固定锚点附近。
- 微软拼音 Consume 有效重跑为 `valid`:Escape 唯一命中并消费,旧过程未调用,对应 EasyX 键消失;7/7 次定位成功、零孤立结果、零丢失且无提交结果。
- 搜狗 Forward 运行确认候选 UI 可见且位置正常,但目标 HWND 未出现 START/COMPOSITION/END;Escape 到达时 tracked state 仍为 Idle,`ComposingOnly` 零命中,定位 6/6、零丢失、收口完整。
- 搜狗最终 Consume 请求按 `ime_condition_not_met` 正确运行时 fail-open:`experimentValidity=runtime_fail_open`、零消费、实际 Forward 一次、EasyX 键出现一次、定位 3/3、零丢失,最终 verdict 为 `MustForward`。
- 2026-10-07 默认 Debug x64 单元测试 15/15、UI fixture 1 轮和 F24 KeyDown A/B 新运行通过;既有 KeyUp 与 baseline mismatch 证据经当前分析器回放保持有效。
- 当前工具环境限制: 自动 F24 runner 已完成构建并打开夹具,但 Codex 非交互工具宿主无法把窗口切到前台,`ClickWindowForActivation` 在发键前失败;该运行没有形成新的 A/B 结论,不记为代码失败,也不覆盖既有有效证据。
- 首次微软拼音复验边界: 旧定位实现导致窗口未响应,失败运行保留于 `TestResults/InputProbe/0B/20261006-231604-636-4bc775a1-a3f8-41b9-b466-1cce740bc195/forward`;该运行只证明诊断死锁,不用于评价 Forward 或微软拼音。
- 验证边界: 自动 `SendInput` 不能替代真实 IME;搜狗不能被要求“规则唯一命中”,因为缺少可证明 Composition 状态时正确行为就是零命中并 fail-open。该 Fix 以固定定位链、原生键观察、零丢失和明确 `MustForward` 结论关闭,不把运行时 Forward 伪装成 Consume。
## 落地信息
- Commit: 待验证与用户确认后提交
- PR:无
- 发布版本:未发布
- 备注: 本修复不进入阶段 1,不实现 FocusManager、生产 InputCollector 或键盘路由。
@@ -0,0 +1,54 @@
# Fix-BUG-20261006-0003
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
## 关联信息
- Fix ID: Fix-BUG-20261006-0003
- 关联 BUG ID: BUG-20261006-0003
- 修复目标: 消除 0B 固定 IME 定位路径的 WndProc 重入自锁,同时保持日志严格序号和可审计关联
- 状态:验证通过(微软拼音 Forward/Consume 与搜狗运行时 fail-open)
- 负责人: Codex
- 分支 / 版本: `master`,未提交工作区
## 修复方案
- 将定位路径拆成三个阶段:
1. 锁内解析并写入原始 `wndproc_enter`,更新 IME/规则状态,复制不可变的定位请求。
2. 锁外调用 `ImmGetContext`、`ImmSetCompositionWindow`、`ImmSetCandidateWindow` 和 `ImmReleaseContext`。
3. 再次加锁校验探测仍启用、HWND 与 `hwndInstanceId` 未变化,更新 `imePositionReady` 和成功计数,并写入独立 `ime_position` 结果记录。
- `ime_position.parentSequence` 指向触发它的 `wndproc_enter.sequence`;结果记录只在 IMM32 调用返回后分配序号。这样 setter 同步重入产生的消息自然位于父 enter 与结果记录之间,不会因“预留低序号、事后补写”破坏 `sequenceStrict`。
- 使用 WndProc 线程局部的 `imePositionApiCallActive` 抑制定位 API 调用期间的递归定位;重入消息仍正常采集、转发和记录,只是不再次调用定位 setter。
- 分析器同时兼容旧版挂在 `wndproc_enter` 上的位置结果与新的 `ime_position` 记录,并校验新记录的 `parentSequence` 存在且严格早于结果记录。
## 影响评估
- 正确性: 移除持锁 IMM32 setter,避免 SRW 自锁;独立结果记录保留调用结果、坐标、触发消息与父序号。
- 并发: `ProbeState::lock` 只保护状态快照与结果提交,不跨越任何定位 setter;完成阶段会拒绝已经 detach 或 HWND 实例变化的过期结果。
- 顺序: 父 enter 先入缓冲,重入消息随后入缓冲,最终结果记录最后入缓冲;全局序号与实际可观察顺序一致。
- 行为边界: 仅影响 `STELLARX_ENABLE_INPUT_PROBE` 且显式开启 0B 人工定位的诊断路径;默认生产构建、公共 API 与正常消息返回策略不变。
- 风险: 不同 IME 仍可能自行调整候选 UI;setter 成功只证明调用链成立,最终位置仍需人工视觉确认。
## 验证结果
- 已完成:
- Debug x64 探测构建完成编译与链接;仅有 EasyX 头文件既有 C4201 警告。
- 旧失败日志重放仍为 268 条严格递增记录、零丢失,并明确判为缺少关闭收口的无效实验。
- 默认 Debug x64 单元测试 15/15 通过。
- Windows PowerShell 5.1 与 PowerShell 7.6.5 对三份 0B 脚本均为 0 个 AST 解析错误。
- `git diff --check` 通过,仅输出仓库换行规范提示。
- 微软拼音 Forward 人工复验通过:窗口保持响应,组合串/候选 UI 位于固定锚点;结构日志为 1142 条严格递增记录、零丢失、规则唯一命中并 Forward、18/18 次定位成功、0 个孤立定位结果、关闭收口完整。
- 有效运行: `TestResults/InputProbe/0B/20261006-234544-007-603391f8-3a14-4c90-ab41-4d891ceb8f46/forward`。
- 人工截图: `manual-evidence/microsoft-pinyin-forward-composition-position-pass.png`,SHA-256 `1447E9F1AAD2C90A787AADC2594C78AE596336FC6D569EA1A22C69B2FCA5C3D5`。
- 微软拼音 Consume 同样保持响应,7/7 次定位成功且 Escape 唯一命中并被消费;有效运行位于 `TestResults/InputProbe/0B/20261007-000152-550-4c83bcda-12e8-4f4e-b616-1d1fe1f56a27/consume`。
- 搜狗 Forward 运行保持响应、定位 6/6;最终 Consume 请求因 Composition 条件不可证明而正确 fail-open,运行仍保持响应、定位 3/3、无孤立结果、零丢失并完整关闭。最终有效目录为 `TestResults/InputProbe/0B/20261007-033454-763-56a2630b-bf2a-4431-b501-38f6ff977dfe/consume`。
- 2026-10-07 默认 Debug x64 单元测试 15/15、UI fixture 1 轮和当前分析器回放通过。
## 落地信息
- Commit: 待验证与用户确认后提交
- PR:无
- 发布版本:未发布
- 备注: 本修复不进入阶段 1,不实现 FocusManager、生产 InputCollector 或键盘路由。