Skip to content

vscode: Builders tree should group by phase (action axis), not area (domain axis); area becomes the row prefix #952

@amrmelsayed

Description

@amrmelsayed

Problem

The Builders tree currently groups by area/* label (via #818, which mirrored the Backlog tree's grouping from #811). One mental model across both views was deliberate at the time. But the two views serve different triage modes:

  • Backlog = "what should I pick up next?" → optimising by domain (area) is right. You scan to the area you want to work on today, then pick from that list.
  • Builders = "what's blocking me right now? where do I need to act?" → optimising by domain misses the question. What you actually want is "show me the builders that need plan-approval", "show me the builders that need a PR merge", "show me the builders just sitting at implement". Those are phase-shaped questions, not area-shaped.

Phase is the action-driven axis for builders. The current area-grouping forces a reverse scan: open every area group, read each row's [<phase>] prefix, mentally collect the ones at a given phase. That's the opposite of how human triage actually works.

Proposed change — swap the axes

Group by phase. Move area into the row prefix.

Visual comparison

Today (area as group, phase as prefix):

▾ VSCODE (3)
    #909 [implement] architect: treat area/*...
    #910 [plan] backlog mine-only
    #918 [implement] search backlog
▾ TOWER (1)
    #805 [verified] worktree symlinks
▸ CROSS-CUTTING (0)

Proposed (phase as group, area as prefix):

▾ IMPLEMENT (2)
    [vscode] #909 architect: treat area/*...
    [vscode] #918 search backlog
▾ PLAN (1)
    [vscode] #910 backlog mine-only
▾ VERIFIED (1)
    [tower] #805 worktree symlinks

Now "all builders at PR-review" is one group expand; "what's in implement vs blocked at plan" is pre-attentive.

What stays the same

Design calls (locked at plan-approval)

These are real choices that benefit from a plan-phase gate rather than being decided ad-hoc during implementation:

1. Phase group order

Alphabetical (IMPLEMENT, PLAN, PR, REVIEW, SPECIFY, VERIFIED) is nonsensical for builder triage. Phases have a natural lifecycle order:

SPECIFY → PLAN → IMPLEMENT → REVIEW → PR → VERIFIED

(Plus protocol-specific phases like AIR's two-phase shape, BUGFIX's investigate/fix, MAINTAIN's distinct flow.)

Recommended: explicit lifecycle ordering, lifecycle-first then any custom protocol phases appended after VERIFIED. Empty groups hide rather than show. Plan should pin the exact order and the empty-group policy.

2. Empty phase groups

If no builder is at SPECIFY, do we render the empty group header, hide it, or render it collapsed?

  • Render empty = always-visible structure, predictable; clutter when sparse.
  • Hide = clean look; user has to mentally model "which phases exist".
  • Render collapsed = compromise; still visible but doesn't take vertical space.

Recommended: hide entirely. Builder counts are typically <15; sparse groups are noise.

3. What does the [<area>] prefix look like?

Options:

Recommended: [vscode] (lowercase, no area/ prefix). UPPERCASE was useful for group headers (matching VSCode's own panel-header convention) but on per-row prefixes it competes visually with the issue number and title for attention.

4. Active vs idle vs blocked distinction across phases

Pre-#810, blocked builders showed bell warning-yellow. Now they show gate-specific icons (book/checklist/play/git-pull-request/verified). The state-color still distinguishes: active = green circle-filled, idle = blue comment-discussion, blocked = warning-yellow per-gate icon.

Under phase-grouping, you'd typically have:

  • IMPLEMENT group: mostly green (active) + maybe blue (idle) rows
  • PLAN group: same (active during planning) — UNLESS the gate is pending in which case the icon would be checklist warning-yellow. But the builder is in the plan PHASE up until the gate is requested, so blocked-on-plan-approval is technically "still phase: plan" with b.blocked set.

This needs clarification:

  • Do blocked builders appear under their pre-block phase group (PLAN with a checklist icon)?
  • Or under a synthetic BLOCKED group?
  • Or split: rows blocked at gate Y appear in PHASE-WHERE-Y-FIRES (e.g., plan-approval → still PLAN group; dev-approval → still IMPLEMENT group)?

Recommended: blocked builders stay in their semantic phase group. PLAN group can contain green-active rows (working on plan) AND warning-yellow blocked-on-plan-approval rows. The icon tells you the state; the group tells you the phase. Don't introduce a synthetic BLOCKED group — it'd compete with the state-color signal already conveyed by icon color.

5. Cross-cutting issue rows

Backlog items with area/cross-cutting show under CROSS-CUTTING today. With phase-grouping, a builder on a cross-cutting issue gets prefixed [cross-cutting] — fine, but verbose. Worth considering [shared] or similar shorthand. Or accept the verbose form for clarity.

Recommended: keep [cross-cutting] verbatim. Shorthand invites confusion ("is [shared] the same as [cross-cutting]?").

6. Persistence of group collapse state

The existing AreaGroupExpansionStore persists area/* group expansion to workspace state. Under phase-grouping:

  • Keep the persistence shape but key by phase name? Different storage keys?
  • Reset to all-expanded since "Pull Requests" group state has no meaning anymore?

Recommended: introduce a parallel PhaseGroupExpansionStore keyed by phase name; old BuilderGroupExpansion (area-based) entries become stale and harmless. Issue #913 is also tracking ephemeral-vs-persistent group state for builders — coordinate this work with that.

Implementation surface

  • packages/vscode/src/views/builders.ts — grouping logic that currently calls groupByArea becomes groupByPhase (new helper or inline). Row label construction (makeBuilderRow / builder-row.ts) gets the prefix swapped from [<phase>] to [<area>].
  • packages/vscode/src/views/builder-row.ts (the builderRowLabel helper from vscode: builder row legibility — phase prefix + gate-specific icons for at-a-glance protocol/state visibility #810) — the phasePrefix becomes areaPrefix; phase is implicit from the group containment.
  • The BuilderGroupTreeItem shape stays the same; just receives a phase name instead of an area name.
  • Backlog tree (backlog.ts, groupByArea) — unchanged.

Acceptance

  • Builders tree groups rows by their porch phase, not their area/* label.
  • Phase groups render in lifecycle order: SPECIFY → PLAN → IMPLEMENT → REVIEW → PR → VERIFIED; protocol-specific phases (e.g. INVESTIGATE, FIX) appended after VERIFIED.
  • Empty phase groups are hidden, not rendered.
  • Each builder row's label uses [<area>] #<id> <title> instead of #<id> [<phase>] <title>.
  • Blocked builders remain in their semantic phase group (no synthetic BLOCKED group); state-color via icon continues to distinguish active/idle/blocked.
  • Backlog tree's area/* grouping is unaffected.
  • Builder counts per group are accurate (the <count> in IMPLEMENT (3) reflects actual children).

Out of scope

Why PIR

Two real design surface areas justify the plan-approval gate:

  1. Phase ordering and empty-group policy — the recommendations above are reasonable defaults but worth pinning before code lands; a wrong call here is a re-do, not a tweak.
  2. Behavioural interaction with vscode: accordion shouldn't affect builder area-group headers, and group expansion state should be ephemeral #913 (accordion / persistent group state) — if vscode: accordion shouldn't affect builder area-group headers, and group expansion state should be ephemeral #913 lands first, coordinate the new phase-group expansion store with the ephemeral-vs-persistent decision there.

And the visual result needs running-app verification before PR — multiple builders across phases, scanning the new tree, confirming the phase-first triage actually feels better in practice. PR-diff-alone is insufficient.

Related

Metadata

Metadata

Assignees

Labels

area/vscodeArea: VS Code extension

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions