Repository navigation
THI-448: show memory footprint and CPU for periscope, its webview and each pane - #53
Merged
tdody merged 52 commits intoNov 1, 2026
Conversation
… server and webview (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ere group, child CPU, sampling lock; no healthz or shell change (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…sctl, no subprocess (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…onsible pid, each pane's subtree, elsewhere (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…iffs a reused pid (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…at most every 0.5 s (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ates comm to 16 characters (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…her, so a failed pass leaves no half-built state (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…t; _name's docstring states proc_name's 32-character cap (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…als (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ich limit tripped (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…r (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rst (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…here measurement lives (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nes, and local open state (THI-448) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…uilt (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…le, and the no-app test stubs _name (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ng forks ps and what a 0% can mean (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… listed side by side no longer read past 100% (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… up past it read as overcommitment (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-out, and measure stamps its sample when it reads (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…cess charged to its parent's group, and a delta past every core is null (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r the same panes (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…icks on a loaded machine still read CPU (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…mem_signal's gb is always a number (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…without the lock (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hange to it fails (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…HI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d cost, the all-panes line and measure(snapshot, panes) (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…popovers (THI-448) ResourcePill, Header's Dropdown, FilterBar and UpdatePill each repeated open/Escape/outside-click wiring; ResourcePill also closed itself when the pill vanished instead of unmounting. Split ResourcePopover out so its open state unmounts with the pill, and replace .update-dd/.res-dd with one neutral .pill-dd class. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…THI-448) The server sends resources.panes every tick but the popover never showed it, so comparing agents against the webview meant adding a dozen per-pane lines by hand. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.res-menu had no max-width, so the label ellipsis never took effect, and no max-height, so a long pane list ran off the screen with no way to scroll to elsewhere/machine below it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…448) Picking a pane line or pressing Escape hid the menu out from under the focused element, dropping focus to <body>; the dialog also had no accessible name for screen readers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…e (THI-448) --fg-3 on the menu's --bg-1 at 11px was about 2.6:1, below WCAG AA's 4.5:1, for text that is data, not decoration. --fg-2 gives about 4.7:1. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.header-res had no CSS rule, so crossing 10% CPU or 1 GB (where fmtCpu/ fmtMem's format breaks) could wrap the header and resize the terminal by a row on that tick. Also moved it after ↻ and the API-error chip so its own width can't shift either of them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-448) claude_proc_for's contract already returns None for a gone process, so the nullable gb path was bloat for a sub-second exit race, never a real state the frontend needed to word around. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
It blamed a missing res on the server's snapshot failing, but resources is null in that case and ResourcePill returns before paneLines runs; the real causes are no pane_pid or the 0.5s throttle, which the backend now keys by pane set. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… 100% cpu (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rged to its nearest surviving ancestor, floored per ancestor, so a Bash tool command's zsh -c chain isn't counted twice (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…y by pid alone fails it; the spec states what the floor protects (THI-448) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
With ~10 agents running, periscope gets laggy. This adds the measurements needed to see where memory and CPU go before optimizing anything (THI-448).
periscope/resources.py, stdlib-only (ctypeson libproc/libSystem). Once per/api/statetick it reads every process'sphys_footprintand CPU time and puts each process in exactly one group: each pane's process tree, the periscope server, the webview, and elsewhere.periscope-appplus every process macOS holds it responsible for: the live WebContent, any WebContent the recycle left parked, GPU and Networking. The app is matched by the kernel's process name, becausepscutscommto 16 characters.git/gh/tscruns and Bash-tool commands show up. A process that ended has its last total charged back to its nearest surviving ancestor (the one whose children time just grew), so nothing is counted twice, including azsh -cwrapper and its command ending together. It's shown as a share of the whole machine (100% = every core busy), not per core liketop, so groups listed side by side read as parts of one total. Samples are keyed by (pid, start time), taken under a lock, at most every 0.5 s. CPU isnulluntil a previous sample exists, and when a reading exceeds every core for the interval.build_statetakes the tick's process snapshot once and passes it in. About 4–6 ms per pass on ~750–790 processes./api/state: each row getsres: {mem_mb, cpu_pct}and the blob getsresources: {server, webview, panes, elsewhere, machine}. Both arenullwhen the tick'spssnapshot fails.▣ 12.6Gheader pill shows what periscope and its panes hold.16.0G RAM · swap 8.6G/10.0G · 39% free · load 9.7. RAM leads because footprints include compressed and swapped pages, so the owners can add up past physical RAM, and that overcommitment is what the line shows.· 630M mem · 0.4% cpu, in a fixed-width box so the header can't re-wrap between ticks.Design:
docs/superpowers/specs/2026-10-30-resource-monitor-design.md. Plan:docs/superpowers/plans/2026-10-30-resource-monitor.md. Related tickets: THI-449 (the webview recycle never fires while you're active) and THI-450 (subprocesses per tick).Test plan
uv run pytest -q: 2080 passed.tests/test_resources.pycovers:zsh -cchain ending, a closed pane's shell, orphans launchd reaped, pid reuse, slow and stale ticks, an impossible delta;Each new test was checked to fail when its fix is undone. An autouse conftest stub keeps every other test off the real machine.
npm test: 1313 passed. Covers formatting, the pill and popover render, the detail-header part, andmemHint.bin/checkis clean, andstatic/distis rebuilt.On the dev server (:17375) with live panes:
res.top.zsh -cwrapper reads ~10% while it runs, with no spike on the tick it ends.Thorough multi-agent review: 17 findings, none blocking merge, all fixed and re-verified.
Assumptions to challenge
resources.pyloads libproc/libSystem at import, andsession_statusnow imports it, so the planned Linux port needs a seam here.responsibility_get_pid_responsible_for_pidis private libSystem API, verified on macOS 27.phys_footprintacross the WebKit helpers is assumed not to double count. That's unproven for the GPU process.ps. It uses the snapshotbuild_statetakes before the fan-out. On a fan-out slower than the snapshot's 1 s cache, Codex detection still re-forksps. That predates this PR and belongs to THI-450.periscope-inputpane count as elsewhere.ps etime. Moving liveness and age offpsis THI-450.🤖 Generated with Claude Code