Skip to content

pa-tui: agents-view summary rows drop the model mix; the inactive line bills the descendant aggregate (operator directive) - #2894

Merged
kevinjosethomas merged 1 commit into
rustfrom
agentsview-summary-nomix-cost
Sep 26, 2026
Merged

kevinjosethomas merged 1 commit into
rustfrom
agentsview-summary-nomix-cost

Conversation

@kevinjosethomas

@kevinjosethomas kevinjosethomas commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

pa-tui: agents-view summary rows drop the model mix; the inactive line bills the descendant aggregate (operator directive)

The operator's 2026-09-26 review of the agents view + sub-agents view (follow-up to #2843). Two fixes, both operator-directive overrides of TS parity (like #2813/#2843): the counts and the per-row Model column the operator confirmed as fine stay untouched.

Fix 1 — the model mix leaves the summary rows (agents_view_forest.rs). Both summary lines appended the descendant tree's model mix to their titles ("2 inactive subagents · claude-opus-4-6, glm-5.3-fast"); the operator asked to hide the models there. Both titles are count-only now ("3, 0 running" / "2 inactive subagents"), the counts stay, the per-child rows keep their own Model column, and the descendant_models multiset goes with its only consumer (model_mix()).

Fix 2 — the aggregate cost shows in the state the operator inspects. Root cause, per the diagnosis the operator requested: the mid-run render was NOT broken — #2843's aggregate printed at the right-aligned Cost cell even with a long mix (reproduced at 120/80/60 cols; the zone truncation always preserved the cell), and #2865/#2866/#2859 did not touch the cost path (git diff f3ae85c..tip on render_row = click-row recording only). The defect was PLACEMENT: the aggregate rode the RUNNING line, a row that only renders while descendants run. In the all-done state the operator actually sees, the only summary row is the INACTIVE line — unbilled (0.0), full-width title, its model mix filling the row exactly where the Cost column should be, which reads as "the aggregate is missing". The inactive line now bills the same status-independent descendant total (parent.descendant_cost) and renders through the same zone-truncated, right-aligned Cost cell path (is_summary_row_identity), so the aggregate prints in both mid-run and all-done frames:

before, all-done: " ▸ 2 inactive subagents · claude-opus-4-6, glm-5.3-fast" (no cost anywhere)
after, all-done: " ▸ 2 inactive subagents $2.00" (aggregate visible)
before, mid-run: " ▸ 3, 0 running · glm-5.3-fast×2, claude- $5.75" / " ▸ 2 inactive subagents" (inactive line unbilled)
after, mid-run: " ▸ 3, 0 running $5.75" / " ▸ 2 inactive subagents $5.75"

TS createSubagentSummaryRow pins recursiveCost: 0; billing both lines is a deliberate Rust divergence (operator directive).

Tests (all green at the PR head): summary_rows_stay_count_only (count-only titles; the tally walk still folds four-level chains; child rows keep their Model cells), running_line_bills_every_descendant_status (both lines bill), running_line_renders_the_aggregate_in_the_cost_column (the inactive line shares the agent rows' Cost column, Age blank), inactive_line_renders_the_aggregate_in_the_all_done_state (the operator's frame: no running line, aggregate on the inactive line), aggregate_survives_the_incident_notice_render_path (#2866), aggregate_survives_the_click_surface_render_path (#2865: the summary row is clickable in the same frame that bills it, and a click expands the list with the aggregate staying put). cargo fmt --all --check and cargo clippy --workspace --all-targets --locked -- -D warnings pass.

Pre-existing base failures (verified on the pristine tip via git stash, untouched by this diff): pa-tui browser::tests::the_opener_resolves_an_absolute_path_and_targets_the_url (no platform opener in the container) and pa-cli agents_view_flash_e2e::the_first_agents_view_render_is_clean_behind_hundreds_of_dead_subagents (roster snapshot serves 1 of 301, daemon-side). All other agents-view e2e suites pass with this diff.


Note

Low Risk
TUI-only agents-view presentation and deliberate TS parity divergence on summary cost; no auth, data, or API surface changes.

Overview
Agents view summary rows no longer append the descendant model mix to running/inactive titles; titles are count-only ("{direct}, {nested} running" / "{n} inactive subagent(s)"), and the descendant_models rollup plus model_mix() are removed. Per-child Model column entries are unchanged.

Cost placement is fixed for the all-done state: the inactive summary row now carries the same descendant-tree aggregate (descendant_cost) as the running line, and render_row bills both summary identities via is_summary_row_identity (right-aligned Cost column, Age blank)—so the aggregate stays visible when no running line renders.

Tests cover count-only titles, dual-line billing, all-done/inactive rendering, and aggregate stability under incident notices and mouse click/expand.

Reviewed by Cursor Bugbot for commit 9168514. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Show descendant aggregate cost on inactive summary rows and drop the model mix

Summary rows in the agents view now render both running and inactive lines through the shared identity predicate in AgentsViewMode.render_row, so the inactive line bills the full descendant cost in the Cost column instead of showing nothing.

  • agents_view_forest.rs removes the descendant model-count map and the model_mix formatter helper; running and inactive summary titles are now count-only.
  • inactive_summary_row now passes the parent's descendant-tree cost to the common summary_row constructor.
  • Tests cover the inactive-only roster case, incident notices, and click-based expansion, all asserting the same aggregate cost and Cost-column alignment.
  • Behavioral Change: running summary titles no longer list descendant model names or counts (running_summary_row, summary_rows_stay_count_only), and the inactive line's cost value changes from unbilled to the descendant aggregate (inactive_summary_row).

Macroscope summarized 9168514.

…e bills the descendant aggregate (operator directive)

The operator's 2026-09-26 review of the agents view + sub-agents view
(the follow-up to #2843):

Fix 1 — the model mix leaves the summary rows. The running line and the
inactive line appended the descendant tree's model mix
("2 inactive subagents - claude-opus-4-6, glm-5.3-fast") to their
titles; the operator wants the models hidden there ("You added this
thing where we list the models inside. You didn't remove that."). Both
titles are count-only now ("3, 0 running", "2 inactive subagents"); the
counts stay (the operator confirmed the counts are fine), the per-child
rows keep their own Model column, and the descendant_models multiset
plumbing goes with its only consumer.

Fix 2 — the aggregate is visible in the state the operator inspects.
#2843 billed the running line only: the aggregate rode a row that
vanishes the moment every descendant finishes, and the inactive line —
the only summary row in an all-done tree — rendered a full-width
unbilled title (its mix filled the row where the Cost column should
be). The inactive line now bills the same status-independent descendant
total and renders through the same zone-truncated, right-aligned Cost
cell path (is_summary_row_identity), so the aggregate prints in both
mid-run and all-done frames. TS parity is deliberately overridden here
(TS createSubagentSummaryRow pins recursiveCost: 0) — operator
directive, like #2813/#2843.

Tests: the count-only titles (the tally walk still folds every depth),
the mid-run aggregate on both lines, the all-done aggregate behind the
inactive line, and the aggregate surviving the #2865 click surface and
the #2866 incident-notice render paths.
@github-actions

github-actions Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Prime Agent performance — partial

PR 9168514c compared with main cd1f215c.

Benchmark execution did not complete successfully. Missing measurements are not performance wins.

Failure diagnostics:

  • pr setup: RuntimeError: pr prepare 0 failed with exit code 1
  • PR: CalledProcessError: Command '['/usr/sbin/runuser', '-u', 'builder', '--', 'npm', 'ci', '--no-audit', '--no-fund']' returned non-zero exit status 1.

See the saved per-trial logs and terminal transcripts for details.

Overall: 0 regressed · 0 improved · 0 no clear change · 42 unavailable.

Metric Main This PR Change
Cold startup 663.2 ms — —
Warm startup 503.3 ms — —
Installation 5.68 s — —
Compressed release artifacts 73.14 MB — —
Installed footprint 595.66 MB — —
Idle memory, summed RSS 656.59 MB — —

Python runtime

Metric Main This PR Change
Python kernel startup 32.7 ms — —
Python cell round trip 0.104 ms — —
Empty bash command 2.1 ms — —
Bash git status 2.8 ms — —
Bash 32 KiB output 2.2 ms — —
35 cells / 9 shell calls 27.6 ms — —
Python interrupt to done 0.532 ms — —
Python state snapshot 9.7 ms — —
Python state restore 124.9 ms — —
Python idle RSS 21.28 MB — —
Python RSS after pandas workload 75.55 MB — —

Session transport

Metric Main This PR Change
Full-history transfers per warm session switch 1.00 transfers — —
Private frame decode, 32 MiB in 8 KiB chunks 14.2 ms — —

UI interactions

Metric Main This PR Change
Resume large session (cold) 1,684.9 ms — —
CPU, resume large session 2,030.0 ms — —
Switch into large session 1,373.3 ms — —
CPU, switch into large session 1,590.0 ms — —
Open agents view from a session 133.4 ms — —
CPU, open agents view 50.0 ms — —
Full agents roster, many sessions 4.03 s — —
CPU, full agents roster 0.95 s — —
Open another session from agents view 1,880.6 ms — —
CPU, open from agents view 1,060.0 ms — —
Reopen resident large session 205.7 ms — —
CPU, reopen resident session 240.0 ms — —
Open subagent session at depth 6 17,442.2 ms — —
CPU, open subagent at depth 6 4,600.0 ms — —
Open chain parent from agents view 2,934.4 ms — —
CPU, open chain parent 1,440.0 ms — —
Scheduled catalog, first request 444.5 ms — —
CPU, scheduled catalog 880.0 ms — —
Scheduled catalog, repeated request 0.5 ms — —
CPU, repeated catalog 0.0 ms — —
Cold worker with three catalog scans 434.9 ms — —
CPU, cold worker and scans 510.0 ms — —
UI memory after interactions 1,715.00 MB — —

Sandbox cost: ~$0.0507 — no inference calls.
Run, logs, and downloadable raw results

Methodology and samples

Main resolved at 2026-09-26T22:10:29.238102+00:00. Harness cd1f215c.
Linux x64, 4 vCPU, 8 GB RAM, 20 GB disk; region us.
Image: node:24-bookworm@sha256:be23f54a88d34e8824c741b19b91064094f92c1c97b194144bfc8b50d67258e2.
Stock tools, skills, daemon, and Python bootstrap enabled; fresh homes and a fixed Git fixture.
Onboarding is dismissed; the editor starts without a selected model or submitted prompt.
Medians shown. Arrows require a 20% timing/memory change plus absolute floors and IQR.
These practical noise floors are not a statistical significance test.
Cold means stopped Prime processes; OS filesystem caches are not flushed.
No model requests or credentials. Installation excludes build/setup time.
Installer tarballs use loopback; npm/Python downloads use the network with fresh caches.
Artifact size counts release tarballs; footprint after first use includes registry packages.
MB is decimal. Summed RSS can double-count shared pages; PSS is recorded when available.
Provisioning, setup, and build durations are recorded separately in the raw results.
Kernel probes use the installed JSONL runtime, outside the TUI/TypeScript host.
Per trial: 50 Python cells, 5 calls per shell case, and one 35-cell mix (9 git status calls).
Cell/shell values are batch means; other runtime timings are single operations.
State fixture: a 10,000-row × 8-column integer DataFrame and a 10,000-integer list.
Restore runs in a fresh kernel, including pandas imports; kernel startup is excluded.
Kernel RSS covers the isolated Python process; loaded RSS follows the pandas workload.
Transport benches run node against the prepared source build, outside the installed home.
The switch benchmark drives one warm switch into a 48k-entry session through a real
daemon and counts full-history crossings: streamed replacement snapshots, inline
replacements, and full-history refetch responses.
Frame decode times one 32 MiB private frame, snapshot-chunk header, pushed in
8 KiB chunks; the wire shape of multi-MB frames on the daemon-worker channels.
UI trials use a fresh fixture set: 194 top-level sessions including one ~40 MB transcript,
40 ledger fan-out children, and a 6-deep subagent chain (~46 spawn edges).
Large fixtures hold 1,999 complete triples (~5 MB JSONL); medium 119; subagents 399 each.
Interactions: cold --resume of a large session, warm /resume switch, left-arrow to agents view,
roster settle with many saved sessions, search-and-open of another large session,
reattaching to that resident session, opening the chain parent, and drilling to depth 6.
Readiness is the rendered transcript tail plus a confirmed editor echo.
CPU metrics sum utime+stime across the whole benchmark-user process tree per interaction.
UI memory sums RSS after the interactions; PTY byte counts are in the raw results.
A separate catalog fixture has 2,300 sessions, 2,298 edges, and 13 paused scheduled-job owners.
Catalog timings cover first/repeated reads and cold worker creation under three pending scans.
All expected jobs and owner metadata are checked; worker readiness excludes TUI rendering.
Costs estimate full sandbox lifetimes at configured rates, including setup and build.
Budget target: $1; not a billing cap. Performance changes are informational.
Failed or incomplete execution fails the workflow; saved artifacts remain available.
Each side stops a phase after 2 identical consecutive failures.
Skipped trials are not attempted samples. Warm startup requires a successful cold launch.

Metric Main successful/attempted PR successful/attempted Main spread PR spread
Cold startup 10/10 0/0 IQR 45.9 ms —
Warm startup 10/10 0/0 IQR 30.6 ms —
Installation 3/3 0/0 range 0.54 s —
Compressed release artifacts 1/1 0/0 — —
Installed footprint 1/1 0/0 — —
Idle memory, summed RSS 10/10 0/0 IQR 7.49 MB —
Python kernel startup 10/10 0/0 IQR 2.2 ms —
Python cell round trip 10/10 0/0 IQR 0.019 ms —
Empty bash command 10/10 0/0 IQR 0.2 ms —
Bash git status 10/10 0/0 IQR 0.1 ms —
Bash 32 KiB output 10/10 0/0 IQR 0.1 ms —
35 cells / 9 shell calls 10/10 0/0 IQR 1.5 ms —
Python interrupt to done 10/10 0/0 IQR 0.049 ms —
Python state snapshot 10/10 0/0 IQR 0.5 ms —
Python state restore 10/10 0/0 IQR 6.3 ms —
Python idle RSS 10/10 0/0 IQR 0.11 MB —
Python RSS after pandas workload 10/10 0/0 IQR 0.18 MB —
Full-history transfers per warm session switch 10/10 0/0 IQR 0.00 transfers —
Private frame decode, 32 MiB in 8 KiB chunks 10/10 0/0 IQR 2.5 ms —
Resume large session (cold) 3/3 0/0 range 343.4 ms —
CPU, resume large session 3/3 0/0 range 140.0 ms —
Switch into large session 3/3 0/0 range 81.0 ms —
CPU, switch into large session 3/3 0/0 range 150.0 ms —
Open agents view from a session 3/3 0/0 range 9.1 ms —
CPU, open agents view 3/3 0/0 range 30.0 ms —
Full agents roster, many sessions 3/3 0/0 range 0.0081 s —
CPU, full agents roster 3/3 0/0 range 0.11 s —
Open another session from agents view 3/3 0/0 range 91.6 ms —
CPU, open from agents view 3/3 0/0 range 110.0 ms —
Reopen resident large session 3/3 0/0 range 25.5 ms —
CPU, reopen resident session 3/3 0/0 range 50.0 ms —
Open subagent session at depth 6 3/3 0/0 range 210.9 ms —
CPU, open subagent at depth 6 3/3 0/0 range 120.0 ms —
Open chain parent from agents view 3/3 0/0 range 111.8 ms —
CPU, open chain parent 3/3 0/0 range 30.0 ms —
Scheduled catalog, first request 3/3 0/0 range 55.8 ms —
CPU, scheduled catalog 3/3 0/0 range 110.0 ms —
Scheduled catalog, repeated request 3/3 0/0 range 0.027 ms —
CPU, repeated catalog 3/3 0/0 range 0.0 ms —
Cold worker with three catalog scans 3/3 0/0 range 115.1 ms —
CPU, cold worker and scans 3/3 0/0 range 30.0 ms —
UI memory after interactions 3/3 0/0 range 35.11 MB —

Failures:

  • pr setup: RuntimeError: pr prepare 0 failed with exit code 1
  • PR: CalledProcessError: Command '['/usr/sbin/runuser', '-u', 'builder', '--', 'npm', 'ci', '--no-audit', '--no-fund']' returned non-zero exit status 1.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9168514. Configure here.

Comment thread crates/pa-tui/src/agents_view.rs
Comment thread crates/pa-tui/src/agents_view_forest.rs
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