Repository navigation
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 Walkthrough
|
Stacked on #16058 (and #15010). Review only the top 2 commits: 9e77cde.
Problem
A plugin has no way to say what it is doing on a thread. A plugin that watches CI, a deploy or a long-running check can act on events, but the user sees nothing in the thread unless they go looking in the plugin's own output. Pi extensions already get short thread statuses (the previous status PRs in this stack), and a plugin should be able to show the same kind of hint next to them.
Why this qualifies
This is the proposal route in CONTRIBUTING, and no maintainer has agreed to it yet. It needs the plugin-system approval on #6714 / #6837 (Pi-style extension API: hooks, UI contributions), like the plugin PRs below it.
It stacks on the declared-contributions PR and reuses the thread status channel from the Pi status PRs (feat(pi): show extension statuses in the thread header (#16045) and feat(mobile): show Pi extension statuses under the thread header (#16046)): no new RPC, no new client subscription. The notifications half of the same proposal is a separate PR on top of this one. If the answer is no, we close both. Previous PR in this stack: feat(web,mobile): show what each plugin adds and what its capabilities allow (#16058).
Fix
contributionStatus.ts): the status source the Pi PR reserved for plugins now exists:{ kind: "plugin", pluginId, name }, keyed by plugin id (the name is display only).PLUGIN_STATUS_CAPABILITY = "status". Older clients drop plugin entries one by one without failing.PluginStatus.ts): host methodsstatus.set/status.clearover the existing plugin host-call channel. Each plugin process has its own store handle per thread, opened in that process's lifetime (the supervisor now hands host methods the calling process's scope), so a disable, removal, crash or server stop clears everything it set on every client. Per process: at most 16 statuses, 10 updates at once and then 2 per second; clears are never limited. Text is normalized like Pi statuses (one plain line, 80 characters, tooltip 240).context.proposed.status.set({ threadId, key, text, tone?, tooltip? })andclear({ threadId, key }), present only with thestatuscapability, which needs"proposedApi": true.status("Shows status on your threads."); before this PR the server refused the capability, so the list showed it by name only.docs/user/plugins.mdgains a "Statuses" section (what shows where, limits, cleared when the process stops).Size: 28 files, +986 / −62; 543 of the added lines are tests.
Evidence
Environment: macOS arm64; this PR on top of the declared-contributions PR.
How to exercise it: isolated
vp run devon fresh state; enable Pi with an extension that callsctx.ui.setStatus(one status,● plan mode); add a scratch plugin with"capabilities": ["status", "actions"], "proposedApi": truewhose thread action callscontext.proposed.status.set({ threadId, key: "ci", text: "CI passing", tone: "success", tooltip }); approve and enable it; run the action on the Pi thread. Before = the declared-contributions PR, after = this PR. Captured on web (Chromium), the built desktop app (isolated profile, its own bundled server), Android (emulator) and a remote browser with a standard pairing overvp run dev --share.Before: adding the plugin is refused (
this server does not support status.), so the thread only shows Pi's statuses.After: the Pi chip and the plugin chip sit side by side; the list names each origin and shows the plugin's tooltip. The trigger reads "Thread status: ● plan mode, CI passing".
Disabling the plugin in Settings clears its chip on every open tab and keeps Pi's; re-enabling and setting it again brings it back; crashing the plugin process clears it again: set → disable → re-enable → crash (MP4).
Android: the plugin chip shows in the status row next to Pi's, without a provider icon; tapping it shows "CI watcher (proof) status" / "Set by the CI watcher (proof) plugin."; disabling the plugin from the web removes only its chip (MP4).
Remote (standard pairing over
--share, HTTPS): the plugin chip appears beside Pi's and clears on disable.Plugin details also explain the capability ("Shows status on your threads."). Light and dark shots of every step for web, desktop and Android are in the media folder.
With three Pi statuses, the header's two visible chips are both Pi's and the plugin's sits in the
+2overflow (the server orders provider entries first); the list still shows it.Checks at this head (
9756bfd76c), re-run 2026-10-05 (CI=true vp test run …, all exit 0):PluginStatus.test.ts,contributions/*,PluginSettings.test.ts: 4 files, 36 tests pass. They cover a plugin beside the provider on one thread and cleared when its process lifetime ends; the 16-status bound, rate limit and unlimited clears (TestClock); refusals (undeclared capability, bad tone, bad keys); the manifest opt-in; a real plugin child process whose status is cleared by disable and by a crash; and the two pool tests (a thread full of plugins still admits its provider; plugins saturating their pool never take provider thread or item capacity).contributionStatus.test.ts(4), client-runtimestate/contributionStatus.test.ts(4, including a rename-only frame re-rendering), webThreadContributionStatuslogic + component (12, including opening the header list with a Pi chip and a plugin chip), mobile status presentation (9). All pass.PluginStatus.test.tsdoes not load.vp run --filtertypecheck for contracts, t3, client-runtime, web and mobile;vp lint --report-unused-disable-directivesandvp fmt --checkon the touched files;vp run knip:check;vp run lint:mobile; web build;vp run build:desktop;node scripts/release-smoke.ts. All pass.Surfaces
docs/user/plugins.md. No internals doc.Not verified
--shareHTTPS origin, not the relay/T3 Connect tunnel.setresolves but the status is not shown (the store's capacity refusals are silent, as for Pi).Claude Opus 5.5 (build), GPT-6.1 Sol (review) and GPT-6 Astra (captures) via T3 Code
🤖 Generated with Claude Code