Skip to content

vscode: re-bucket Builders tree — actively-communicated blocked builders sort below active, truly-blocked stays on top #798

Open

Description

@amrmelsayed

Problem

The Builders tree treats every gate-blocked builder as equally urgent — they all sort to the top with a bell icon and a wait-time suffix. In practice, "blocked" splits into two very different cases:

  1. Truly blocked — the builder hit a gate (e.g. plan-approval) and nobody has touched it since. This is the one that wants attention.
  2. Blocked while the architect is actively in dialog with it — the gate is open but the engineer is mid-conversation, sending messages, iterating. Surfacing this row at the top with the same urgency as a truly-stuck builder is noise; the engineer already knows it's there because they're typing to it right now.

The current top-bucket placement makes the second case feel like an alarm that won't quiet.

Current state

  • Sort order in packages/vscode/src/views/builders.ts:23–28 (orderForDisplay):
    1. blocked (any b.blocked truthy), sorted by blockedSince ascending.
    2. idleWaiting (isIdleWaiting(b, now)).
    3. active (everything else).
  • OverviewBuilder.lastDataAt (@cluesmith/codev-types, exposed via overview.ts:73) already tracks when Tower last received output from the builder's shell — updated whenever the architect's input echoes back or the agent emits anything.
  • isIdleWaiting(b) (packages/core/src/builder-helpers.ts:30) returns true for non-blocked, non-complete builders silent past IDLE_WAITING_THRESHOLD_MS = 5 minutes (line 19). The same threshold is the natural cut-off for "actively communicating" applied to blocked builders.

Proposed behavior

1. New predicate

Add isActivelyCommBlocked(b, now) in packages/core/src/builder-helpers.ts:

export function isActivelyCommBlocked(b: OverviewBuilder, now: number = Date.now()): boolean {
  if (!b.blocked) return false;
  if (!b.lastDataAt) return false;
  return now - new Date(b.lastDataAt).getTime() <= IDLE_WAITING_THRESHOLD_MS;
}

Co-located with isIdleWaiting so the threshold policy stays in one place.

2. New four-bucket sort order

Top-down:

  1. Truly blocked — b.blocked && !isActivelyCommBlocked(b). Sorted longest-blockedSince first.
  2. Idle waiting — isIdleWaiting(b) (unchanged).
  3. Active — everything that doesn't match the others (unchanged).
  4. Actively-comm blocked — isActivelyCommBlocked(b). Sorted most-recent-lastDataAt first (so the one you're typing into right now is at the bottom-most slot — close to where the input cursor sits in the terminal pane).

3. Visual treatment

  • Truly blocked: existing bell icon + wait-time suffix; unchanged.
  • Actively-comm blocked: distinct icon (e.g. $(comment-discussion) instead of $(bell)) and the row label still surfaces the gate (blocked on <gate>) so the user remembers which gate is in play — but the visual weight is de-emphasised (no wait-time scream).
  • Idle-waiting and active: unchanged.

4. Threshold reuse

Use the existing IDLE_WAITING_THRESHOLD_MS = 5 minutes — no new tunable. A blocked builder you haven't typed to in >5 minutes promotes itself back to "truly blocked" (top bucket), which matches user intent: dialog has stalled, the gate is now really blocking.

Acceptance criteria

  • isActivelyCommBlocked() predicate added to packages/core/src/builder-helpers.ts, alongside isIdleWaiting.
  • orderForDisplay in views/builders.ts returns four buckets in the order above.
  • Truly-blocked rows render unchanged (bell icon, wait-time suffix).
  • Actively-comm-blocked rows render with a distinct icon and no wait-time suffix; gate label preserved.
  • Sort ordering reactively updates as lastDataAt ticks past the threshold (no extra polling — relies on existing SSE refresh cadence).
  • No additional Tower instrumentation needed; uses existing lastDataAt.

Out of scope

  • A separate threshold for "actively communicating" different from IDLE_WAITING_THRESHOLD_MS (intentionally — one threshold, one policy).
  • Manual "I'm working on this" flag.
  • Auto-collapse / auto-pin of the actively-comm group.

Activity

  1. self-assigned this
    on May 21, 2026
  2. added
    area/vscodeArea: VS Code extension
    projectNew project or feature
    and removed on May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area/vscodeArea: VS Code extensionprojectNew project or feature

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions