feat(sessions): a ceiling on waiting for an answer that never comes (v0.293.0) - #536
Merged
Conversation
…v0.293.0) The idle janitor skips a session blocked on a question or approval — that wait is real. But nothing expires an Inbox card, so the exemption had no floor: the wait could be permanent. Live expresstech was holding a `support` session 66h after its question was asked, with two more unanswered since 07-28; each pins a claude process (~300MB) and a concurrency-cap slot for as long as nobody looks. New setting (Settings → Runtime, default 72h — the age at which escalateStalePrompts already stops nagging and treats a prompt as dead): past it, the session is closed and its card cancelled, which is also what makes the card dismissable instead of hanging. 0 restores wait-for-ever. - Clock runs from when the OLDEST pending card was RAISED, not from session idleness: the claim is "nobody answered this in three days", and a blocked session is quiet by definition. - A session with someone ATTACHED is never cut — a human is right there. - Interactive lane only; unattended runs already have unattendedMaxHours (24h), which overrides a pending block. - Audited as session.reaped reason:'blocked-timeout' with the wait duration, so it's distinguishable from an ordinary idle reap. 6 new reaper assertions (29/29): old block reaped + card cancelled, fresh block kept, attached kept, approvals counted like questions, and 0 = never. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vikasprogrammer
force-pushed
the
feat/blocked-ceiling
branch
from
August 3, 2026 08:05
a6dcd1c to
23f6532
Compare
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 last thread in this morning's arc. #521 fixed a finished run held open by an unanswered card; this fixes a running one.
The idle janitor deliberately skips a session blocked on a question or approval — that wait is real, and cutting it would throw away work someone is about to unblock. But nothing expires an Inbox card, so the exemption had no floor and the wait could be permanent.
Found on live expresstech while updating it:
ses_b1ac7cb96d53a72fses_09b95256dcb2ad1eses_56ac68828a99a366Each blocked session pins a
claudeprocess (~300 MB) and a concurrency-cap slot for as long as nobody looks at the Inbox.The ceiling
New setting — Settings → Runtime → "Close a session waiting on an unanswered question after (hours)", default 72 h. That default isn't arbitrary: it's
STALE_PROMPT_MAX_MS, the age at whichescalateStalePromptsalready stops nagging and treats a prompt as dead. The two now agree about when a prompt is abandoned.Past the ceiling the session is closed and its card cancelled — which is also what makes the card dismissable rather than hanging in the Inbox forever.
Three deliberate boundaries:
unattendedMaxHours(24 h), which overrides a pending block.Audited as
session.reapedwithreason: 'blocked-timeout'and how long it waited, so it's distinguishable from an ordinary idle reap when reading the trail.0restores the old wait-for-ever.Tests
6 new assertions in the reaper suite (29/29), covering the cases that actually distinguish this from a blunt timeout:
Also fixed a trap in that suite: section 4 pinned
aliveNames()to its own two panes, so any session a later section created was markedcrashedby the crash sweep before the idle sweep saw it — the new section failed for reasons that had nothing to do with the code under test. Stub restored per section.159/159 conformance, 18/18 tier-A, 18/18 capability registry.
typecheckand both builds clean.Note on expresstech
Three questions there have been unanswered since 07-28 / 07-31. This stops them holding processes, but somebody should still answer them — the ceiling is a janitor, not a substitute for the Inbox.
🤖 Generated with Claude Code