Skip to content

test(tui): fix flaky needs_animation_gates_prompt_history_tick_delivery - #65

Merged
zy1233 merged 1 commit into
docs/plan-record-settings-and-cleanupfrom
fix/pi-pager-flaky-history-tick-test
Oct 31, 2026
Merged

zy1233 merged 1 commit into
docs/plan-record-settings-and-cleanupfrom
fix/pi-pager-flaky-history-tick-test

Conversation

@zy1233

@zy1233 zy1233 commented Oct 30, 2026

Copy link
Copy Markdown
Owner

结果

修了随机失败的测试 pi-pager app::app_view::tests::needs_animation_gates_prompt_history_tick_delivery(app_view_tests.rs:474,tick() must poll the history daemon and deliver results)。只改了测试,没有改产品代码。测试数量不变(5651),所以不用改 tui_baseline.toml 与 docs/baselines/tui.md。

触发这次修复的失败:run 41097019349(PR #58)。

根因:测试的假设有竞争,产品行为没有问题

  • HistorySearchState::activate 先给 daemon 线程发 SetItems,马上把 daemon 已发布的快照拷贝到界面一侧(last_gen = snapshot.generation)。
  • poll()(tick() 调它)只在 generation 变大时返回 true。
  • 旧测试要求 app.tick() 为 true 且 result_count() == 2。
    • daemon 慢:拷贝到的是空快照,之后 tick() 看到新 generation,通过。
    • daemon 快(先于拷贝发布):拷贝到的已经是 2 条,之后没有新 generation,tick() 永远是 false,1000 次、约 1 秒后失败。
  • 产品里这两种情况结果一致:activate 已经把数据交给界面,调用方在激活后会重绘。所以不是产品竞争,是测试等了一个在「daemon 快」时永远不会出现的事件。

证据(确定性复现):在 activate_inner 的 refresh_items 与拷贝之间临时加 sleep(50ms)(模拟 daemon 先到),旧测试第 1 次就失败,同样的 panic。移除后复原。

修法

activate 之后调用 history_search.update_query("")。daemon 在拷贝之后一定会发布一个更大的 generation(两种情况都成立),tick() 就一定会交付它。等待改成最长 30 秒的截止时间,只用来结束挂死;通过时立刻返回,不会变慢。没有加长 sleep。

证据(本机,4 vCPU,内核 6.12;测试用 cargo nextest run -p pi-pager --lib -E 'test(needs_animation_gates_prompt_history_tick_delivery)' --stress-count N)

条件 修之前 修之后
8 个占满 CPU 的循环,1000 次 2 次失败(0.2 %);之前另一次 194 次中 1 次 两组各 1000 次,0 失败
4 个循环 + taskset -c 0(单核),300 / 1000 次 300 次 0 失败 1000 次 0 失败
32 个循环,1000 次 0 失败 未测
强制 daemon 先到(activate 里 sleep(50ms)) 第 1 次就失败 30 / 30 通过
强制 daemon 慢(daemon 里 sleep(100ms)) 未测 20 / 20 通过

说明:单核加载并不会提高失败率;32 个循环时失败率反而为 0(主线程更不容易在发送与拷贝之间被抢占)。失败率最高的是 8 个循环。所以「修之后 0 / 2000」的证据力有限,真正的证据是上面的确定性复现。

门禁(同 tui-ci.yml 的内容):

  • python scripts/tui_baseline.py check:全部 ✅(含 cargo check --workspace --tests,0 warnings)。
  • python scripts/tui_baseline.py test --suite pi-pager(已清掉 PI_HOME 等变量):5651 通过,0 失败,12 跳过。
  • rustfmt --check 通过;cargo clippy -p pi-pager --lib --tests 退出 0,这个文件没有新告警(pi-pager 里原有的 10 条告警不是这个改动引起的)。
  • ruff check .、ruff format --check . 通过。

没有 Windows / WSL,没有跑。已清掉测试写到 ~/.pi-python 的东西。

只报告、没有修(同样写法的等待)

  1. views/history_search.rs:633-640、:643-651:activate_and_poll、query_and_poll 只等 100 次 × 1 ms,超时不报错,直接往下走。预算很小,机器一卡就会让后面的断言失败。
  2. views/history_search.rs:688-696(refresh_items_and_query_are_applied_together):同样 100 × 1 ms,而且断言写在 poll() 为真时。
  3. app/agent_view/prompt.rs:1303(poll_results)与 app/agent_view/prompt_stash.rs:196(await_history_results):各 500 × 1 ms,守着同一个 daemon。它们看 result_count、selected_text,不依赖 poll() 的返回值,所以不会有这次的竞争,但预算只有约 0.5 秒。
  4. app/app_view_tests.rs:841-849(tick_scroll):200 × 5 ms,约 1 秒。
  5. scrollback/search.rs:721、:823、:1169 与 app/agent_view/links.rs:1471:1000 × 1 ms。它们都等 poll() 返回 true,所以如果后台已先发布,与这次是同一类竞争,需要再看。

最近 TUI CI 的随机失败

main 上最近 20 次 TUI CI 都是 success(一次被取消,没有失败)。三个最近的失败:

Open in Web Open in Cursor 

…result

activate() copies the daemon snapshot at once, so a quick daemon had already
delivered both entries and tick() never saw a newer generation. Ask the daemon
for a new generation after activate and wait for tick() to deliver it.

Co-authored-by: z123385708 <[email protected]>
@zy1233

zy1233 commented Oct 31, 2026

Copy link
Copy Markdown
Owner Author

Stack #66, bottom to top: #58, #59, #56, #57, #60, #61, #62, #63, #64, #65. One atomic squash merge (gh stack merge).

Position 10 of 10. Base: docs/plan-record-settings-and-cleanup (#64). Head b414624 -> e354af5. The commit has the same patch (equal patch id). It is applied on top of the stack so that the stack can merge in one operation; the CI run of this head checks the whole tree.

Local checks of the stack tip before the push (Windows and WSL Linux): ruff check and ruff format --check pass; scripts/tests 251 passed on Windows and 257 passed on Linux; tui_baseline.py check (gates, cargo check, cargo check --workspace --tests) 0 warnings, residue ceilings and dependency graph at their ceilings (leader 239, x.ai 282, 970 packages); tui_notices.py check 984 of 984; tui/Cargo.lock is exact. The Rust tests ran only in CI.

@zy1233
zy1233 merged commit 032ddbb into main Oct 31, 2026
8 of 14 checks passed
@zy1233
zy1233 deleted the fix/pi-pager-flaky-history-tick-test branch October 31, 2026 03:17
zy1233 added a commit that referenced this pull request Oct 31, 2026
…ne on main (#67)

Ten PRs (#56 to #65) were merged into main with one `gh stack merge`
(stack #66, squash). main went from 7cb9e23 to 032ddbb.

- Execution log: new section 10.31 (stack order, the eight rebuilt
  heads, conflicts, checks before the merge, CI on main), contents list,
  section 10 title and intro, revision log.
- Plan: header and "current state" at r28; phase D started; D.5 and D.8
  ticked; the sentences that said "not merged" now say "merged"; the
  section 10 index; the revision summary; link definitions.
- docs/baselines/tui.md: numbers from the TUI CI run on main (8,558
  passed, residue ceilings 239 and 282, test and job times).

Documents only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants