fix(timeboxing): reselecting a day no longer strips the date card of its controls - #242
Merged
Merged
Conversation
…its controls Live incident, 2026-08-31 22:57. A /timebox session posted its Stage-0 date card as its own session-thread root, the user picked "Tomorrow" in the dropdown, and the card lost every button. The reselect handler redrew the card and then relabeled the thread root — but in the slash-command route those are one Slack message, so the text-only relabel landed on the card it had just redrawn and wiped its blocks. The session then sat at Stage 0 waiting for a Confirm click that had no button, which from the channel is indistinguishable from the bot having died. The relabel now recognises the aliased layout and stays its hand: the card already displays the selected day, so only a separate root message needs its label kept in step. The regression test drives the real handler with the incident's own message layout and asserts over minted action ids that the last write to the card still carries Confirm — and a second test pins the relabel for genuinely separate roots, verified to fail when the guard over-suppresses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The incident
2026-08-31, 22:57, live
/timeboxrun: the Stage-0 date card appeared, the user picked Tomorrow — Tuesday 1 September in the dropdown, and the card lost every control. What remained was a bare "🟡 Timeboxing session for Tomorrow — Tuesday 1 September" title. The session kernel was healthy and waiting for a Confirm click that no longer had a button — from the channel, indistinguishable from the bot having died.Root cause
In the slash-command route the date card is posted as its own session-thread root (
forced_thread_root = origin_processing_msg["ts"]), someta.thread_ts == prompt_ts._handle_timebox_date_reselectredraws the card, then relabels "the thread root" — a text-onlychat.updateaimed at the very message it just redrew, which replaces the card's blocks with nothing. The handler logs nothing on success, so the log went silent at exactly the moment the UI did.The Confirm path survives the aliased layout (its outcome lands on a fresh thread reply); only the dropdown reselect destroys it, which is why this lived until the dropdown's first real use.
The fix
The relabel now recognises when the session root is the card and skips itself — the redrawn card already displays the selected day, so only a genuinely separate root needs its label kept in step.
Tests
test_reselect_on_a_card_that_is_its_own_thread_root_keeps_confirm— drives the real handler with the incident's message layout; asserts over minted action ids that the last write to the card still carries Confirm. Watched fail on the unfixed code, reproducing the incident's clobbering write verbatim.test_reselect_with_a_separate_thread_root_still_relabels_it— pins the relabel for the two-message layout; verified to fail when the guard over-suppresses (per the break-it-on-purpose rule in CLAUDE.md).tests/replay/test_timeboxing_incident_20260829.py.Not in scope
The deeper oddity — one message serving as ack, progress card, outcome card and thread root in the slash path — is left as is; changing it moves session identity (
session_key = channel:thread_root). The running dev bot also still needs a restart to pick this up; it has been serving 186ebcf since 19:28.🤖 Generated with Claude Code