Skip to content

fix(slack): stop a progress card whose message will not accept edits - #747

Merged
JSap0914 merged 2 commits into
devfrom
codex/slack-stream-lifetime
Sep 11, 2026
Merged

fix(slack): stop a progress card whose message will not accept edits#747
JSap0914 merged 2 commits into
devfrom
codex/slack-stream-lifetime

Conversation

@JSap0914

Copy link
Copy Markdown
Collaborator

Stacked on #746.

A Slack stream expires after about five minutes. A long job keeps working, so the progress card falls back to editing the message the stream owned — that part is deliberate and stays. What was missing is the case where that message is also gone.

In the 2026-09-11 incident the fallback chat.update returned message_not_found, nothing promoted that to a stop, and the idle loop re-dirtied the snapshot 3.2 seconds later. The same doomed request went out 47 consecutive times across two and a half minutes, with no bound except the end of the job (serve.log 19570–19662).

chat.appendStream  → message_not_in_streaming_state   (once)
chat.update        → message_not_found                (× 47)

What this does

endLive(reason, gone) ends the live loop once, clears both timers, and counts slack.progress.stream_state_lost.

  • message_not_found and cant_update_message end it immediately. The message is unreachable; no retry changes that, and finalizing a card known to be gone spends no request proving it again.
  • Three consecutive failures of any other kind end it too. A transport that has rejected the same request three times running will not take the fourth.
  • Rate limits are excluded. The embargo already spaces those and the content is retried.
  • startIdle's callback re-checks the flag, which could previously be raised while its timer was already pending.

A dead card is never replaced by a second one, and the card freezes at its last known state rather than disappearing.

What this deliberately does not do

Cap successful fallback edits. An earlier draft allowed one catch-up edit after expiry and then froze, which is what the issue suggested — but that undoes 35e3a994, where an expired stream keeps its card current through a long run. The storm came from repeated failures, not repeated successes, so the bound belongs on failures. a stream closed after five minutes continues editing its own status through a long job and retains live status through minute nineteen both still pass unmodified.

This also does not stop the misroute. The card's clock is stamped once at handle creation (progress-activity.ts:122) and never reset, so "경과 531초" in a two-second-old thread was an existing handle seen at a new address — a destination-binding question (#746), not a lifetime one.

Testing

tests/unit/slack-progress-stream.test.ts + slack-progress-lifecycle + slack-progress-observer: 110/110, no existing case modified. Two new: SPS-744 for the gone-message path (one edit discovers it, five more minutes add nothing, no postMessage, no timers left) and SPS-744 for the consecutive-failure bound. npm run gate:all — 23/23.

Refs #744

suji lee added 2 commits September 11, 2026 22:00
A Slack stream expires while a long job keeps working. The card then falls back
to editing the message the stream owned, which is what keeps a long run visible.
Nothing handled the case where that message is also gone: chat.update returned
message_not_found, the idle loop re-dirtied the snapshot 3.2 seconds later, and
the same doomed request went out 47 consecutive times over two and a half
minutes with no bound but the end of the job (#744).

message_not_found and cant_update_message now end the live loop immediately -
the message is unreachable and no retry changes that - and three consecutive
failures of any other kind end it too, since a transport that has rejected the
same request three times running will not take the fourth. Rate limits are
excluded: the embargo already spaces those and the content is retried.

Successful fallback edits are untouched, so an expired stream still keeps its
card current through a long job. Finalizing a card known to be gone spends no
request proving it again. A dead card is never replaced by a second one.

Refs #744
@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 341e1369-b133-491d-b44c-649d99cce879

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Base automatically changed from codex/slack-request-scoped-destination to dev September 11, 2026 13:19
@JSap0914
JSap0914 merged commit d44e9df into dev Sep 11, 2026
11 checks passed
@JSap0914
JSap0914 deleted the codex/slack-stream-lifetime branch September 11, 2026 13:19
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.

1 participant