Skip to content

Sidebar rows show the state of the work, and every task's git facts stay fresh - #249

Merged
pallaoro merged 2 commits into
mainfrom
i-often-see-other-solutions-showing
Sep 11, 2026
Merged

pallaoro merged 2 commits into
mainfrom
i-often-see-other-solutions-showing

Conversation

@pallaoro

Copy link
Copy Markdown
Member

A sidebar row could only say whether an agent was attached. A task merged last week, one that never started, and one genuinely waiting on you all wore the same green "idle" dot (.tstatus.idle).

The row's trailing slot now answers the question that remains once no agent is attached: what state is the work in. It renders triage.bucket with triage.reason as the tooltip.

bucket glyph colour
merged_done GitMerge dim
merged_unfinished GitMerge amber
open_pr GitPullRequest green
unmerged_no_pr GitCommitVertical dim
uncommitted GitCommitVertical amber
orphan CircleAlert red
active / not_started nothing

A live agent keeps the slot (blue running, amber needs-you, hollow when triage says stalled, mirroring the card's .ring.stalled). The leading agent glyphs are unchanged.

Two things had to be true first

prState was a write-only-"merged" field. Its only two writers both wrote the literal "merged", so open and closed were unreachable despite being in the type — .pr.open in the CSS had never once rendered. detectMerged already parsed the state from the gh pr view it was paying for, then the caller returned early on !merged and threw it away. It now returns that state, at no new I/O.

Git and PR facts only refreshed for the open task. CH.gitStatus is fired from the task panel alone, so every other row showed a snapshot from whenever it was last opened, or nothing at all. worktree-sweep.ts makes that one refresh path with two triggers (the RPC, plus a 90s background pass started beside the reap timer), sharing a 60s per-task throttle so they cannot double-probe. Each pass takes 12 worktrees round-robin, skipping live tasks (their events already cover them) and terminal merged ones. Recording PR facts is split from moving a card to Done: noting an open PR is always safe, while the column move keeps its old guards.

Also fixes a collision the glyph exposed: .task-trail is absolutely positioned, so names ellipsized underneath it. The row now reserves that space constantly, so changing state never reflows it.

Verification

  • 474 tests pass, full workspace typecheck clean, zero new lint diagnostics.
  • Ran the real app against an isolated scratch data dir seeded with one task per bucket; confirmed the real database was untouched. Hover swap verified with dispatched mouse events (glyph out, trash in and clickable, glyph returns).
  • detectMerged run against live GitHub: add-search-bar-for-tasks-at{merged:true, prNumber:61, state:"MERGED"}; show-sidebar-scrollbar-opacity-only-on{merged:false, prNumber:114, state:"OPEN"}. That second case is precisely what the old code discarded.

Known gap: worktree-sweep.ts has no unit test. The throttle, the batch cursor, and the record-PR-state-without-moving-the-column split are the logic worth pinning down.

🤖 Generated with Claude Code

pallaoro and others added 2 commits September 11, 2026 15:37
…tay fresh

A sidebar row could only say whether an agent was attached. A task merged last
week, one that never started, and one genuinely waiting on you all wore the
same green "idle" dot.

The row's trailing slot now answers the question that remains once no agent is
attached: what state is the WORK in. It renders `triage.bucket` — merged, open
PR, commits ahead, uncommitted, orphan — with `triage.reason` as the tooltip.
A live agent keeps the slot (blue running, amber needs-you, hollow when triage
says stalled), and the leading agent glyphs are untouched.

Two things had to be true first.

`prState` was a write-only-"merged" field. Its only two writers both wrote the
literal "merged", so `open` and `closed` were unreachable despite being in the
type — `.pr.open` in the CSS had never rendered. `detectMerged` already parsed
the state from the `gh pr view` it was paying for, then the caller returned
early on `!merged` and threw it away. It now returns that state, at no new I/O.

Git and PR facts also only refreshed via CH.gitStatus, which the renderer fires
from the open task's panel alone, so every other row showed a snapshot from
whenever it was last opened, or nothing. worktree-sweep.ts makes that one
refresh path with two triggers — the RPC and a 90s background pass — sharing a
60s per-task throttle so they cannot double-probe. Each pass takes 12 worktrees
round-robin, skipping live tasks and terminal merged ones. Recording PR facts
is split from moving a card to Done: noting an open PR is always safe, while
the column move keeps its old guards.

Also fixes a collision the glyph exposed: .task-trail is absolutely positioned,
so names ellipsized underneath it. The row now reserves that space constantly,
so changing state never reflows the row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pallaoro
pallaoro merged commit 825bb29 into main Sep 11, 2026
1 check passed
@pallaoro pallaoro mentioned this pull request Sep 13, 2026
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.

1 participant