Skip to content

fix(timeboxing): reselecting a day no longer strips the date card of its controls - #242

Merged
hugolytics merged 1 commit into
mainfrom
fix/timebox-date-reselect-keeps-card
Sep 1, 2026
Merged

fix(timeboxing): reselecting a day no longer strips the date card of its controls#242
hugolytics merged 1 commit into
mainfrom
fix/timebox-date-reselect-keeps-card

Conversation

@hugolytics

Copy link
Copy Markdown
Collaborator

The incident

2026-08-31, 22:57, live /timebox run: 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"]), so meta.thread_ts == prompt_ts. _handle_timebox_date_reselect redraws the card, then relabels "the thread root" — a text-only chat.update aimed 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).
  • Neighbouring suites green: 91 passed incl. 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

…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>
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