Add docs/agent-handoff (backlog, execution state, verification, platform plan, per-task evidence), repo AGENTS.md, and architecture notes updated for the dual-renderer design.
57 lines
3.9 KiB
Markdown
57 lines
3.9 KiB
Markdown
# P0-02 合并重复的 `MainWindow.eventFilter` — 执行证据
|
||
|
||
日期:2026-09-16(无人值守轮次)
|
||
平台:Windows 11 10.0.26200 x64 · Python 3.10.21(`.venv`)· PyQt6/Qt 6.10.0/6.10.2 · 离屏 `QT_QPA_PLATFORM=offscreen`
|
||
|
||
## 根因(源码确认)
|
||
|
||
- `MainWindow` 类体内定义了两个 `eventFilter`(行 3697 与 3763):后定义者覆盖前者,前者的
|
||
`_active_streams` 守卫是死代码。
|
||
- 生效版本(3763)用 `btn_send.isEnabled()` 做守卫,而 `set_send_button_state` 只切换
|
||
图标、从不禁用按钮 → 守卫恒真 → 流式生成中按 Enter 会落入 `send_message` 的中断路径
|
||
(触发停止),与注释声称的「生成时按回车无效,防止误触」相反。
|
||
|
||
## 修复摘要(仅 `ui/views/main_window.py` 事件过滤逻辑)
|
||
|
||
- 删除行 3763 的重复 `eventFilter`(及其后不可达的两行过期分节注释)。
|
||
- 保留行 3697 处为 `MainWindow` 唯一 `eventFilter`:Enter(无 Shift)→ `send_message(from_enter=True)`
|
||
并消费事件(一次按键至多一次调用);Shift+Enter → 返回 False 放行换行;其他对象/事件交父类。
|
||
- `send_message(self, from_enter: bool = False)`:函数顶部为发送规则单一实现:
|
||
1) `btn_send` 禁用 → 一律不发送;
|
||
2) `from_enter=True` 且当前会话在 `_active_streams` → 直接返回(Enter 不参与停止/中断语义);
|
||
3) 按钮点击路径行为完全不变(流式中点击 = 原有红色停止按钮中断语义,含 Fix B/C)。
|
||
- `_update_send_button_state` 未改(其语义与规则一致)。
|
||
|
||
## 定向测试(命令 / 退出码 / 结果)
|
||
|
||
| 命令 | 退出码 | 结果 |
|
||
|---|---|---|
|
||
| `python tests/test_main_window_event_filter.py` | 0 | ALL PASS(18 项断言) |
|
||
| `python tests/smoke_offscreen.py` | 0 | ALL PASS: 8/8 |
|
||
| `python tests/smoke_mode.py` | 0 | ALL PASS |
|
||
| 回归 `python tests/test_config_isolation.py` | 0 | ALL PASS(14 项) |
|
||
| 回归 `python tests/test_error_persist.py` | 0 | 39 PASS |
|
||
| 回归 `python tests/smoke_bash_panel.py` | 0 | 116 PASS |
|
||
|
||
注:`smoke_offscreen.py` / `smoke_mode.py` 自身只重定向数据库、未设置 `HAOCODE_CONFIG_FILE`
|
||
(不在 P0-02 允许修改清单内)。本轮运行时在进程环境显式导出指向临时配置的
|
||
`HAOCODE_CONFIG_FILE`;P2-04 聚合入口将按子进程强制注入临时环境,彻底闭环。
|
||
|
||
## 完成证据对应(test_main_window_event_filter.py)
|
||
|
||
- **AST 静态断言**:解析 `ui/views/main_window.py`,`MainWindow` 类体内 `eventFilter` 定义恰好 1 个(行 3697);
|
||
- **Enter 可发送**(A1–A5):空闲 + 按钮可用 + 有文本 → 一次 Enter 恰好一次 `send_message(from_enter=True)`
|
||
调用(计数 wrapper 包住真实实现),流同步注册、输入框清空、错误路径自清理;
|
||
- **Enter 被禁用**(B1–B3):`btn_send.setEnabled(False)` → 至多一次调用且无流、输入内容保留(规则在 `send_message` 内生效);
|
||
- **流式时 Enter 被拦截**(C1–C3):注入假流 → Enter 后假流对象未被替换、字段未被改动(未触发中断)、输入保留;
|
||
- **Shift+Enter 换行**(D1–D3):零调用,事件放行到输入框(光标处插入 `\n`)、无流;
|
||
- **其他键/事件交父类**(E1–E2):按 `a` 正常插入字符、零发送调用;
|
||
- **一次按键至多一次调用**:A/B/C/D 各用例均以调用计数断言(全部 ≤1 且语义正确)。
|
||
|
||
## 行为变化说明
|
||
|
||
- 流式生成中按 Enter:旧(生效)代码会触发停止/中断;新代码 no-op(Enter 只管发送,
|
||
停止只走按钮)。这与被覆盖版本注释中声明的原始意图(「生成时按回车无效,防止误触」)
|
||
和 P0-02 硬约束(「流式生成时不得发送」)一致,属本任务预期的确定性修复。
|
||
- 发送按钮点击路径(含流式中点击 = 中断):逐行未动。
|