Skip to content

fix(tui): ignore a queued PinChanged once the thread is gone - #146

Merged
allenhutchison merged 1 commit into
mainfrom
fix-139-threadview-race
Oct 27, 2026
Merged

allenhutchison merged 1 commit into
mainfrom
fix-139-threadview-race

Conversation

@allenhutchison

@allenhutchison allenhutchison commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Human overview

Fixes a flaky TUI test (test_nothing_is_marked_read_while_the_thread_is_scrolled_up) that failed intermittently with NoMatches: 'ThreadView'. The cause was in app code, not the test: a queued scroll-position message was handled during teardown, after the thread widget was gone. The handler now ignores it.

Closes #139

AI reviewer

Root cause. The failing traceback (windows run 40552203583) is not in the test body: it is raised from run_test.__aexit__ (raise self._exception), via on_thread_view_pin_changed -> _note_viewed -> _scrolled_up -> self.thread -> query_one(ThreadView). The test ends right after a scroll_end/open_conversation, which can leave a ThreadView.PinChanged message queued; Textual dispatches it while the screen is being torn down, so the query finds no ThreadView. The issue's guess (test racing the screen mount) is not what happens: the test already waits on body_text before touching the thread, and the body is reached via app.thread only after that.

Change.

  • src/pepper_cli/tui/app.py: on_thread_view_pin_changed returns early when self.query(ThreadView) is empty (query, unlike query_one, does not raise). Nothing to read or mark with no thread, and _run("publish") / refresh_chrome have nothing new to do at teardown.
  • tests/test_tui_live.py: new deterministic regression test test_a_pin_change_delivered_after_the_thread_is_gone_is_ignored (opens a topic, removes the thread, delivers PinChanged). The flaky test itself is untouched, so its assertions are unchanged.

Verification. New test fails without the app.py change (NoMatches) and passes with it. Could not reproduce the original flake locally (30 runs unloaded, 30 under 8 busy-loop CPU hogs: 0 failures before the fix), so the regression test is the proof. After: target file x50 with -p no:randomly: 0 failures. uv run ruff format --check, ruff check, ty check src/pepper_cli/: clean. uv run pytest --cov --cov-report=json:coverage-summary.json: 2485 passed, 2 skipped. -n auto not run (xdist is not a dev dependency here).

Where to look. The guard sits on the one handler that reads self.thread from a message that can outlive the widget; other handlers that call self.thread from queued messages were not audited. Not refactored: until/_quiet wait helpers are already shared via tests/tui_support.py.

Checklist (repo-specific)

  • uv run ruff format --check and uv run ruff check clean (no new lint ignores)
  • uv run ty check src/pepper_cli/ clean
  • uv run pytest passes; coverage floor in .claude/maintainerd.json not lowered
  • Server-contract changes: N/A, no API change
  • No token or credential is logged, printed, or put in a fixture
  • README and the bundled SKILL.md updated if commands or output changed: N/A, no command change

🤖 Generated with Claude Code

https://claude.ai/code/session_01QNDeh9nWVhiZkwR7qyrAZT

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge.

Summary

Prevents a queued ThreadView.PinChanged message from crashing the app after the thread widget is removed.

  • The TUI ignores queued scroll cues when their thread is gone.

Reviews (1) · Last reviewed commit: "fix(tui): ignore a queued PinChanged onc..." · Reviewed by Greptile

@vycari-review

vycari-review Bot commented Oct 27, 2026

Copy link
Copy Markdown

Vycari Review (shadow)

Shadow round 1 on 486c9d2. This comment is non-gating and needs no response: it is collected to measure agreement with the current reviewer. The round's verdict is the Vycari Review (shadow) check run.

Open: P0 0 · P1 0 · P2 0 · confirmed fixed 0 · withdrawn 0

Judge's notes

Incomplete: 1 stage call(s) failed (hunter:general). No candidate findings were raised.

No open findings this round.

@allenhutchison
allenhutchison added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 6bbf488 Oct 27, 2026
5 checks passed
@allenhutchison
allenhutchison deleted the fix-139-threadview-race branch October 27, 2026 22:30
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.

Flaky: test_nothing_is_marked_read_while_the_thread_is_scrolled_up — NoMatches 'ThreadView' on default screen

1 participant