Skip to content

refactor(agent-status): one worktree rollup and one clock (PR 3) - #19788

Closed
brennanb2025 wants to merge 2 commits into
brennanb2025/agent-status-2b-renderer-subscribesfrom
brennanb2025/agent-status-3-shared-rollup
Closed

brennanb2025 wants to merge 2 commits into
brennanb2025/agent-status-2b-renderer-subscribesfrom
brennanb2025/agent-status-3-shared-rollup

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​318 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​318
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​275 $\color{#cf222e}{\Huge{\mathbf{−}}}$​111 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​164

Skeleton PR — plan only, no implementation yet. Stacked on #19787.

What this step does

Collapses the worktree card rollup and the 30-minute decay from three implementations into one in src/shared.

The three copies

Copy Lines Reads
renderer/src/lib/worktree-status.ts 222 the sidebar store
main/runtime/runtime-worktree-status-projection.ts 255 the hook snapshot
mobile/src/worktree/agent-row-display.ts 118 worktree ps rows

The mobile copy hand-copies the 30-minute constant rather than importing AGENT_STATUS_STALE_AFTER_MS, so the three can drift by edit as well as by logic.

isFreshNonDoneAgentStatus in shared/agent-status-freshness.ts is already shared and is the model — the rollup should sit beside it.

Why this is last

Only after PR 2b do all three read equivalent rows. Sharing the rollup before that unifies the arithmetic over inputs that still disagree, which hides the disagreement rather than removing it.

Watch item

Mobile currently ignores structuredHostOwned, so an owned structured row decays to idle there while the desktop keeps it. Pre-existing, not a regression from PR 1a — and this is the step where it stops being true, since the shared rule already handles the owned case.

Dependency

Stacked on #19787 (PR 2b).

@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2b-renderer-subscribes branch from a34c37f to c76845d Compare September 10, 2026 00:02
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-3-shared-rollup branch from 6c8c44a to f8d0d3f Compare September 10, 2026 00:02
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2b-renderer-subscribes branch from c76845d to bebdf84 Compare September 10, 2026 03:41
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-3-shared-rollup branch from f8d0d3f to 4772ad3 Compare September 10, 2026 04:22
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Live Electron validation of the whole stack — 7/7 PASS

First live run over any of these five steps, against the top of stack (4772ad30bb), which contains 1a → 1b → 2a → 2b → 3. Isolated dev instance, hidden renderer, CDP screenshots only. Instance ownership proved before any capture: devBranch = 4772ad30bb, private renderer port, and an /@fs body check (status codes alone are unreliable) — so these are provably screenshots of this stack.

# Question Verdict
1 Structured session shows a row and settles to done PASS
2 Exactly ONE row per session PASS
3 PTY terminal agent unaffected PASS
4 worktree ps agrees with the sidebar, drops on tab close PASS
5 Dashboard popout shows structured sessions (new) PASS
6 Reload restores both row types PASS
7 Dismiss removes the row from worktree ps immediately PASS

The two that mattered most

Q5 is a new capability, not a preservation. The dashboard popout has never shown structured sessions — it's a separate React root that never mounted the renderer's status bridge. Removing the IPC filter is what gives it rows for the first time.

popout

Q7 is an intended behavior change, and the CLI output verifies it rather than a screenshot: worktree ps --json went from 2 agents to 1 the moment the row was dismissed, with the PTY row gone and the structured row untouched. Previously a dismissed row lingered until the pty exited. One store means one dismissal.

Provenance is correct on both row types

Q3 and Q6 confirm the two paths stayed distinct through the refactor: the PTY row carries terminalHandle and no structuredHostOwned; the structured row carries structuredHostOwned: true and no terminalHandle. That is PR 1b's handle-based join and PR 2a's ownership mapping both intact, after a reload.

pty alongside structured
reload

Working → settled, and CLI/sidebar parity

working
settled
ps parity

Two findings that are NOT stack defects

  • On first popout open only the structured card showed. Not a dropped row — the published snapshot contained both; the dashboard's own showIdle: false filter hid the idle-bucketed PTY card. Driving a PTY turn rendered it. Pre-existing filter behavior.
  • The sidebar's default compact display mode renders no trailing controls, so the Dismiss (X) is unreachable until you switch to full. Not introduced here, but worth knowing the affordance is compact-mode-invisible.

Gaps, stated rather than implied

Dev-mode instance under electron-vite (no packaged build). Claude structured route only — the Codex structured route was not exercised. Local folder workspace only: no SSH/remote host, no mobile client. A late turn hit an account-level 429 after all evidence was captured; the row still settled correctly.

@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2b-renderer-subscribes branch from bebdf84 to 910d2aa Compare September 10, 2026 08:11
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-3-shared-rollup branch from 4772ad3 to 417235e Compare September 10, 2026 08:11
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2b-renderer-subscribes branch from 910d2aa to f159550 Compare September 10, 2026 18:32
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-3-shared-rollup branch from 417235e to 9f8560d Compare September 10, 2026 18:32
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2b-renderer-subscribes branch from f159550 to 8d0acb5 Compare September 11, 2026 22:53
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-3-shared-rollup branch from 9f8560d to bf7fb55 Compare September 11, 2026 22:55
Merge Sim added 2 commits September 11, 2026 20:36
Names the three derivations and their line counts, records that mobile
hand-copies the staleness constant, and says why sharing the rollup only makes
sense after the inputs agree.
…s decay into src/shared

The worktree card status was derived three times: the renderer's
worktree-status.ts, main's runtime-worktree-status-projection.ts, and mobile's
agent-row-display.ts, which hand-copied the 30-minute constant.

Two modules now own it, beside agent-status-freshness.ts:
agent-status-row-display.ts holds the staleness decay and where a stale row
lands (idle / unverifiable); worktree-status-rollup.ts holds the priority
ladder. The renderer's hand-ordered if-chain became a fold over that ladder and
main's private priority table was deleted; the wire subset keeps its order, so
worktree ps rolls up exactly as before.

Mobile is not a pure refactor. Two corrections, both to the behavior the
desktop has always had: a structuredHostOwned row no longer decays to idle on
the phone, and a restoredUnconfirmed row never reads as active (a contract
change - today's host drops those rows before the wire).

The evidence clock does not reach mobile: RuntimeWorktreeAgentRow carries no
evidence stamps, so the shared rule falls back to updatedAt, mobile's existing
clock. Forwarding them would trade a reconnect error for a clock-skew one, so
the wire is unchanged and absence degrades to the ordinary window.

Also: mobile was never walled off from src/shared (metro already watches it and
~20 modules value-import from it), and main's mergeWorktreeStatus was dead.
Nothing pinned the worktree ps rollup order, so a reordered ladder passed the
whole runtime aggregator; runtime-worktree-status-projection.test.ts now pins it.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-3-shared-rollup branch from bf7fb55 to d6b0839 Compare September 12, 2026 03:36
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing as superseded, for the same reason as #19786 and #19787 — the lane was rebuilt and this stage is now written against a contract that postdates this branch.

What changed underneath it. The shared rollup and clock work now sits after the canonical store (#20717) and its producers, and depends on host-owned child work keyed by a host-minted childWorkId rather than on renderer-derived rows. The structured clock half is folded into the child-producer stage, which puts real turn and completion clocks on structured rows instead of overloading one timestamp.

Salvage. The shared selectors and rollup intent are carried into the reader-cutover stage, where all four readers move onto one set of host-fed selectors and the two parallel child-freshness rules become one shared decay rule.

Branch brennanb2025/agent-status-3-shared-rollup is retained; it is 339 commits behind main and rebasing it is not the intended path.

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