Skip to content

refactor(agent-status): the renderer subscribes instead of deriving (PR 2b) - #19787

Closed
brennanb2025 wants to merge 2 commits into
brennanb2025/agent-status-2a-converge-pane-keyfrom
brennanb2025/agent-status-2b-renderer-subscribes
Closed

brennanb2025 wants to merge 2 commits into
brennanb2025/agent-status-2a-converge-pane-keyfrom
brennanb2025/agent-status-2b-renderer-subscribes

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 5 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​444 $\color{#cf222e}{\Huge{\mathbf{−}}}$​97 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​347
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​389 $\color{#cf222e}{\Huge{\mathbf{−}}}$​85 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​304

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

What this step does

The actual unification: main starts publishing structured rows to the renderer, and the renderer stops deriving its own.

The two filters removed

Both were added by PR 1a and are the only thing keeping main out of the renderer's lane. Each is pinned by a test, so this step starts by turning those tests red on purpose:

  • main/startup/main-window-agent-status.ts — the if (structuredHost) return guard above the agentStatus:set sends. It sits above both the main window and getDashboardPopoutWindow(), so removing it is also what first gives the dashboard popout structured sessions (it has never shown them).
  • main/ipc/agent-hooks.ts — the .filter((entry) => entry.structuredHost === undefined) on agentStatus:getSnapshot.

Writers retired

Per the audit table in the design doc:

Writer Disposition
Command Code output seeds, parked-pane seeds, pty-exit removal delete — main already emits the same facts
structured bridge status writes delete — main now publishes the row
launch placeholder seeds keep for now (deferral, not principle)
dismissal, acknowledgement, unmount keep — user facts and component lifecycle
remote-runtime OSC parse keep, fenced behind the host's published row
web-session mirror receipt clock keep — the decay rule needs both clocks from one machine

The structured bridge's unmount cleanup becomes a tab-close signal to the host rather than a status write.

What "one writer" means after this

Not zero renderer writers. The IPC applicator becomes the single writer for observed status; four categories stay on purpose. Of those, only the launch placeholder seeds are a deferral — main holds the launch config and could seed them. That is the next thing to remove after this step, not part of it.

Open decision

The Command Code done-settle window is renderer policy with no main equivalent. This PR either moves it into main's detector or leaves it — and says which.

Dependency

Stacked on #19786 (PR 2a). Removing these filters before the two derivations converge renders one session twice and shifts the chat's elapsed clock.

@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2a-converge-pane-key branch from a267639 to 56e5046 Compare September 10, 2026 00:02
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2b-renderer-subscribes branch 3 times, most recently from bebdf84 to 910d2aa Compare September 10, 2026 08:11
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2a-converge-pane-key branch from 08f5918 to 9d90a71 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-2a-converge-pane-key branch 2 times, most recently from 9c9451e to d95f3f1 Compare September 11, 2026 22:50
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2b-renderer-subscribes branch from f159550 to 8d0acb5 Compare September 11, 2026 22:53
Merge Sim added 2 commits September 11, 2026 20:36
Both filters are pinned by tests, so the step starts by turning those red on
purpose. Records that removing the set-guard is also what first gives the
dashboard popout structured sessions, and that one-writer means one writer for
observed status, not zero renderer writers.
… rows

Main's two half-migration filters are gone, so a locally hosted structured
session's row now reaches both renderer windows over `agentStatus:set` and comes
back on the `agentStatus:getSnapshot` replay pull. Removing the send guard is
also what first gives the dashboard popout structured sessions at all.

Removing them was not enough on its own. A structured pane key resolves to
nothing in the renderer's terminal-tab routing index — an `agent-session` tab
lives in `unifiedTabsByWorktree` — so the applicator held every published row as
`pending` forever, with every test still green. The new routing module resolves a
structured row against that tab, and requires one: deliberately narrower than
`worktree ps`, because it is what the sidebar already showed, and because a
terminal tab in chat view mode is backed by a structured session too and already
has its own row.

`structuredHost: 'owned'` maps back to `structuredHostOwned` so the freshness
bypass still fires; `terminalResumeEligible: false` is kept so no sleeping record
offers to relaunch a chat as a TUI; `allowOlderTimestamp` is dropped because main
stamps a forward-only `receivedAt`. The row's name is read from the session tab
rather than put on the wire.

The bridge stops projecting for a local target only. A remote host writes into
ITS store and nothing mirrors structured rows down, so it stays that session's
writer. Its unmount teardown stays for both lanes: the host's close drops its row
through `dropStatusEntry`, which emits no renderer clear.

Command Code output seeds, parked-pane seeds and the pty-exit removal are NOT
deleted, against the plan. Main emits Command Code working/done as side-effect
FACTS the renderer turns into the write, gated on renderer-only pane ownership
state; nothing puts them in the hook store. The done-settle window stays with
them. The doc records both, with the mechanism.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-status-2b-renderer-subscribes branch from 8d0acb5 to b009f95 Compare September 12, 2026 03:36
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing as superseded — the work this PR does is now a later stage of the rebuilt lane, to be written against a contract that did not exist when this was authored.

Why it cannot simply be rebased. This was written before AgentStatusSubject (#20531) froze the identity contract, and before the canonical store (#20717) and its first producer (#20718) landed. The renderer-subscribes change now depends on host-fed selectors that are only being built in the stages ahead of it, and the branch is 339 commits behind main.

Where it lands instead. Reader cutover is its own stage: all four readers — sidebar, orca worktree ps, mobile and dashboard — move onto the same host-fed selectors, the renderer bridge writer is deleted, and today's two parallel child-freshness rules collapse into one shared decay rule. That stage is explicitly gated: it cannot start until the legacy-producer allowlist is empty on native, WSL and SSH, with no local-only waiver.

Salvage. Its missing-PTY-means-idle default is deliberately not carried forward as the remote rule — loss of contact is never evidence of process death, and the vocabulary is live / unverifiable / exited. That decision is recorded so it is not reintroduced.

Branch brennanb2025/agent-status-2b-renderer-subscribes is retained.

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