Skip to content

fix(tapbacks): a failure is said even if Blip closed while it ran - #139

Merged
nixfred merged 1 commit into
nixfred:mainfrom
ianswope:fix/tapback-failure-survives-close
Oct 8, 2026
Merged

nixfred merged 1 commit into
nixfred:mainfrom
ianswope:fix/tapback-failure-survives-close

Conversation

@ianswope

@ianswope ianswope commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Follow-up to the "not addressed" note on #138.

Cause

The imsg-react Process lived in the BlipView that started it. The popout's surface and the app window are both destroyed on close, and on a macOS 27 gateway a run takes 15-75 s. Close Blip mid-run and the Process went with the view, so its exit, and the reason on stderr, never reached the status line.

Fix

  • The run moves to BarWidget (runTapback, reactProc), which outlives both surfaces. One run at a time, as before.
  • Every exit emits tapbackExited(chat, ok). On success a view on that chat settles its pending pill exactly as before.
  • A failure is held in tapbackFailures[chat] until a view showing that conversation takes it (takeTapbackFailure): immediately if it is on screen, otherwise when the conversation is next opened, in the popout or the window. This replaces the view-local tapbackNote. A new tapback drops an older failure for the same chat.
  • The not-started guard (sawExit) moves with the Process.
  • imsg-react: on macOS 27 the newest-message check now runs before Messages is touched, so a tapback on an older message is refused in about a second without bringing Messages forward or opening the conversation.

Verified

  • On a 27.0.1 gateway, a tapback on an older message showed tapback: on this macOS only the newest message in a conversation can take a tapback in the status line; react.log: not-last exit=65.
  • Deployed with a shell restart; no plugin errors in the qs log; the popout renders.
  • tapbacks.test.ts asserts the new wiring, including that BlipView no longer owns reactProc. test_react.py 60 pass (the not-last refusal now also asserts the conversation was never opened). bun test 750 pass.

The imsg-react run lived in the BlipView that started it, and both the
popout surface and the app window are destroyed on close. On a macOS 27
gateway a run takes 15-75 s, so closing Blip mid-run took the Process and
its exit with it: the failure, with its reason on stderr, was never shown.

The run now lives in BarWidget (runTapback, reactProc), which outlives
both surfaces. Every exit is a tapbackExited(chat, ok) signal; a failure
also waits in tapbackFailures[chat] until a view showing that
conversation takes it (takeTapbackFailure), at once if it is on screen,
otherwise when the conversation is next opened in either surface. A new
tapback drops an older failure for the same conversation.

imsg-react: on macOS 27 the newest-message check now runs before
Messages is touched, so a tapback on an older message is refused in about
a second without bringing Messages forward.

Verified: on a 27.0.1 gateway, a tapback on an older message showed
"tapback: on this macOS only the newest message in a conversation can
take a tapback" (react.log: not-last exit=65). test_react.py 60 pass,
bun test 750 pass.
@ianswope
ianswope requested a review from nixfred as a code owner October 8, 2026 03:07
@nixfred
nixfred merged commit fc3d74a into nixfred:main Oct 8, 2026
1 check passed
@nixfred

nixfred commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Merged, thank you Ian, and for closing your own loose end from #138 the same night. Moving the run into the bar widget is the right owner: a 15-75 s run on macOS 27 was always going to outlive a closed window, and a failure that waits for its conversation beats one that vanishes. Refusing an older message before Messages comes forward is a nice touch too. 750 tests and every bridge test green; live on my Mac and both Linux machines.

nixfred added a commit that referenced this pull request Oct 8, 2026
LR-T: blip, change-log, weekly-notes, #139, tapbacks, ianswope
LR-D: blip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeRMdPWNC2myz9U8hEeeCi
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