Repository navigation
Add Info tab row and section slots that core uses - #6117
Merged
Merged
Conversation
The thread Info tab now renders only through two registries: app.slots.experimental_threadInfoRow (details block) and app.slots.experimental_threadInfoSection (below it). Core's eleven rows and five sections are registrations of the same shape, merged with plugin registrations by order, so the tab keeps today's order and behaviour. Info's own building blocks ship to plugins as experimental_InfoSection (heading, count, collapse), experimental_DetailRow, experimental_InfoList, and experimental_InfoListRow; core's rows and sections draw with the same components. Section collapse is keyed per registration (core ids unchanged, plugin sections as <pluginId>/<id>). The Automations plugin registers the first plugin section: automations whose execution.targetThreadId is the open thread. automations_list and `bb automation list --target-thread` gain the target-thread filter. Fixes #5554 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Info tab slot section pushed frontend-registration.md to 528 lines, over the shipped-skill reference cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DetailRow and InfoDetailRow were two row components: the Info tab, plugins
and most rows used InfoDetailRow, while Branch, Archived and the commit
dialog reached past it to style the value cell themselves. DetailRow is now
the single row ({ label, icon?, align?, children }) that core and
experimental_DetailRow share. Branch and Archived truncate through an inner
span, and the commit dialog's vertical changed-files row draws its own
markup with the shared label and value classes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Info rows and sections took one flat order number, and every core row's number was documented, so renumbering core would move plugin entries. They now take a required group plus an optional order within it, sorted with the same comparator thread actions use (moved to lib/grouped-placement.ts). bb's groups ship as experimental_THREAD_INFO_ROW_GROUPS (thread, environment, git, pullRequest, lifecycle) and experimental_THREAD_INFO_SECTION_GROUPS (related, changes, storage). Core keeps today's order; Automations joins related ahead of Child threads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run-only signals never change the automation rows the section shows; every write that does also publishes automations-changed. Ignoring automation-runs-changed halves the section's refetches while runs happen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexandre-vl
added a commit
to alexandre-vl/bb
that referenced
this pull request
Oct 22, 2026
get-bb#6117 renders Info sections through slots, so RelatedThreadsSection no longer takes sectionId.
ymichael
added a commit
that referenced
this pull request
Oct 22, 2026
## Human comments
## What was wrong
GitHub pull request status was core. The daemon ran `gh`, the server
assembled and watched the status, and the app, SDK, CLI and Tasks and
GitHub plugins each reached it through core routes and commands. Core's
PR UI couldn't leave core because there was nothing outside core to read
from.
## What changed
**New bundled, default-enabled plugin: `plugins/github-pr-status`
(`bb--github-pr-status`)**
- `host.ts` / `host/git-host.ts`: the in-worktree lookup moved from
`packages/host-workspace/src/git-host.ts`. It covers the current branch,
upstream and fork remotes (`getPullRequestTarget`), the merge-queue
GraphQL query, and `gh pr ready|ready --undo|merge`. It runs on the
environment's own machine with the daemon's shell `PATH`.
- `server.ts`:
- resolves the environment;
- serves a keyed `pullRequests` read, one row per environment id (up to
200), so a list of rows loads in one request;
- serves `markReady`, `markDraft` and `merge`, each of which re-checks
state before acting;
- registers `bb pull-request show|ready|draft|merge <environment-id>`.
- `store.ts` is the single place reads are cached (10 s), re-checked
while watched, and published when they change.
- PR snapshots are kept by PR URL, so environments sharing a PR share
one snapshot. Persistence, cross-row reuse and webhooks can feed this
one place later.
- While a row is watched (`bb.rpc.experimental_whileWatched`), it
re-checks every 30 s when checks are pending, auto-merge is on, the PR
is queued or mergeability is unknown. That is the same rule as core's
`pull-request-watch.ts`.
- Other triggers match main's:
- `status-changed` and environment create/delete fetch fresh;
- a completed turn re-checks through the 10-second cache;
- `git-refs-changed` only marks the cached row stale;
- `metadata-changed` re-checks only when the environment's branch
actually changed (one environment read, no `gh`);
- in the app, a cached row is re-fetched on mount, and again when the
page is shown if it's older than 30 s (1 h once merged or closed). Main
got this from TanStack's `refetchOnMount: "always"` and focus refetch.
Here it uses only the public read hook's `refetch`, in
`pull-request-queries.ts`.
- It invalidates the row's tag only when the row changed, and never for
a result a viewer is already receiving.
- `contract.ts` is the public contract. The read returns the fully
assembled shape the UI renders: state, attention, checks, review,
mergeability, auto-merge and merge queue.
**Watched reads**
- `bb.rpc.experimental_whileWatched` comes from `bb/5939-recut`,
mechanism only: the `plugin-read` subscription target, the server
registry and producer lifecycle, app query hooks holding the
subscription, per-target subscribe validation, and fake-host support.
- Left out: the SDK `experimental_watchRpc` /
`experimental_watchDiscovery`, the CLI `--watch` flags, and
plugin-signal scoping.
**Core reads and acts only through the plugin's public contract**
- The composer banner segment, Info row and thread view use
`experimental_useRpcKeyedQuery` / `experimental_useRpcMutation` with
`pluginId: "bb--github-pr-status"`
(`apps/app/src/hooks/queries/pull-request-queries.ts`). These are the
same calls another plugin makes, so moving the UI later is a cut and
paste.
- The app's RPC hook host now allows those hooks outside a plugin slot
when `pluginId` is explicit.
- `bb thread show`, Tasks (`listTaskPullRequests`, now one batched read)
and the GitHub plugin's `pullForThread` call the same RPC.
**Deleted from core**
- The server PR watcher, route, cache, `environment-pull-request` tag
and subscription target, and the PR action branches.
- The SDK `environments.pullRequest` / `markPullRequestReady` /
`markPullRequestDraft` / `mergePullRequest`, and `bb environment
pull-request …`.
- The `ThreadPullRequest` / `GitHostPullRequest` domain schemas.
- The daemon's `workspace.pull_request` and
`workspace.pull_request_action`. **`HOST_DAEMON_PROTOCOL_VERSION` 250 →
251**, compatible `[250, 251]`. 250 stays compatible because the server
no longer sends either command. 246–249 stay removed, as #6018 intended.
**`experimental_useSidebarThreadPullRequest`**
- Removed from `@get-bb/plugin-sdk/app`, the Plugin Guide and the docs.
- Published bundles keep working: the app installs
`retiredPluginSdkAppMembers` on the plugin runtime, backed by the
plugin's read. It returns `{ isLoading: false, pullRequest: null }` when
the plugin is disabled.
- **Builds never fail on it.** bb builds git-sourced marketplace plugins
at install and update, and rebuilds path and builtin plugins when the
SDK changes. So the `bb-plugin-build` runtime shim keeps the name in its
`@get-bb/plugin-sdk/app` export list. Importing it is a build warning
with the replacement call, which `bb plugin build`/`dev` print and the
server writes to the plugin's log.
- Migration guide:
`bb-plugin-authoring/references/frontend-pull-requests.md`. The runtime
shim is recorded in `docs/api_to_audit.md`.
- Plugin SDK 0.6.72.
**No wake.** Plugin host calls never wake a suspended host. A read on an
asleep or offline machine returns `unavailable`, which the UI renders as
no PR. Ready, Draft and Merge fail with "<machine> isn't available …".
**Behaviour change:** reads no longer wake a suspended host. On main,
`workspace.pull_request` used the `work` wake policy, so opening a
thread whose machine was suspended woke it.
## How you verified
**Request measurement in the real app.** Main and this branch ran as two
`pnpm start:worktree` instances side by side. Each had a thread on a
project checkout of PR #6117's branch while its checks were pending
(`checks_pending`, 0 failed). In each, a headless page opened the thread
and every `/api` request was recorded for 3 minutes, both runs at the
same time:
| | PR requests | when (s) | all `/api` requests |
|---|---|---|---|
| main | 6 × `GET /environments/:id/pull-request` | 1, 32, 64, 95, 127,
158 | 84 |
| branch | 6 × `POST /plugins/bb--github-pr-status/rpc/pullRequests` |
1, 33, 64, 96, 127, 159 | 88 |
The other `/api` differences changed from run to run (hosts, server-move
and execution-options in this run; system config, providers and timeline
in another). They come from background activity in the freshly restarted
branch instance and its extra sidebar threads, not PR traffic. An
earlier run found two bugs that are fixed here with tests:
- a re-check right after open published a result the opening read was
already fetching, costing one extra request;
- a watch that went idle when checks finished never resumed when a new
CI run started.
**Busy thread.** Main and this branch ran side by side again, each with
a thread on its own scratch PR in a private test repo
(`ymichael/test-project` #303 and #304, both closed and their branches
deleted afterwards). Each thread was open in a page. Both received the
same agent turn at the same moment: three commits, a `git fetch`, a
third commit between them, and a `git push`, which also started the
repo's CI. Over 5 minutes, a process monitor counted every `gh` started
by each instance, attributed by process ancestry and confirmed by
working directory. The page counted PR read requests. Two runs gave
identical counts:
| | `gh` calls | PR read requests |
|---|---|---|
| main | 7 (4 `pr view`, 3 merge-queue `api graphql`) | 4 |
| branch | 5 (3 `pr view`, 2 merge-queue `api graphql`) | 2 |
Before this fix the branch also re-checked with `gh` on every
`git-refs-changed` and `metadata-changed`, so a thread that commits and
pushes would have exceeded main.
**Focus refresh.** Main (`f909788625`) and this branch ran side by side
with the same open PR. A page on each hid and showed itself 5 times over
4 minutes, with gaps from 20 s to 60 s, so some shows were under the 30
s stale time and some over. A process monitor counted `gh` per instance:
| | `gh` calls | PR read requests |
|---|---|---|
| main | 5 | 5 |
| branch | 5 | 6 |
The extra read is the realtime connection resuming at the same moment
the page showed. Both reads hit the plugin's 10 s cache, so there's no
extra `gh`.
**Published plugins that use the retired hook.** On the review app, I
installed four git-sourced Community marketplace plugins that call
`experimental_useSidebarThreadPullRequest`. Each went through `bb plugin
install <plugin>@bb-community`, so bb cloned and built it at its
published commit. Each was then used as the sidebar thread list:
| Plugin | Commit | Builds | Warning in `plugin.log` | Renders PR badges
|
|---|---|---|---|---|
| t3sidebar | `8b535729dd` | ✅ | ✅ `src/ThreadCard.tsx:6` | ✅ |
| dockside | `e8736f3858` | ✅ | ✅ `components/inbox/thread-card.tsx:7` |
✅ |
| radar-sidebar | `9a20bfab7e` | ✅ | ✅
`components/radar/RadarThreadRow.tsx:23` | ✅ |
| thread-inbox | `c0981fb6fe` | ✅ | ✅ `src/ThreadCard.tsx:18` | ✅ |
gtd-sidebar fails to install on main too ("Could not resolve
@get-bb/plugin-sdk"), so it's unrelated. This test caught a real bug: T3
Sidebar crashed with a React hook-order error. React Compiler only
treats `use…` names as hooks, so it cached the
`experimental_useRpcKeyedQuery` call inside `useEnvironmentPullRequest`
and skipped it on re-renders with unchanged arguments. Core's own
callers pass a fresh options object on every render, which hid it. The
hooks are now imported under `use`-prefixed local names, and all four
plugins render.
**UI parity.** Screenshots of the same thread on main and on the branch
show the same composer banner segment (PR #6117 with the Merge split
button) and the same Info row (#6117, Open, Checks running). `bb thread
show` printed the PR through the plugin. On this PR, `bb pull-request
draft <env>` converted it to draft. The banner then showed "PR #6125 ·
Draft" with Mark ready, and clicking Mark ready showed the loading
toast, made the PR ready on GitHub, and returned the banner to the open
state with Merge.
**Tests**
- New plugin tests (75):
- host-entry fork, upstream and detached cases using a fake `gh`, moved
from host-workspace;
- the parse, action-argument and merge-queue table;
- the assembly table, moved from the server;
- store polling, publish-on-change and the viewer race;
- server reads, refusals, the no-wake refusal and the CLI.
- Also added: the `bb-plugin-build` retired-import warning test, the
focus-refresh staleness table and refetch tests, Tasks batched reads and
the disabled-plugin case, CLI `thread show` through RPC, and a hub test
for the `plugin-read` target.
- After merging #6135: `pnpm exec turbo run typecheck lint
--filter='...[origin/main]'` passes 162/162, and `turbo run test
--filter='...[origin/main]'` passes 115/115. That includes #6135's
host-workspace default-branch tests and the host-daemon
`watch-manager-startup` setup-window test. The host-workspace merge kept
#6135's code. This branch only removes the PR lookup from it.
**Not covered**
- `gh` ran only on a local host. Remote hosts use the same plugin
host-artifact path as the other host plugins but weren't exercised.
- Merge wasn't run against a real PR. It uses the same `gh pr merge
--<method>` arguments, covered by the host tests.
> AGENT GENERATED
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ymichael
added a commit
that referenced
this pull request
Oct 23, 2026
## Human comments
## What was wrong
Thread action tabs, navigation-page fixed tabs, and core Info/Diff used
different registration and opening paths. That made tab context and
lifecycle inconsistent and prevented plugins from owning core pinned
tabs. Saved layouts also needed to survive the transition, plugin
loading, visibility changes and reloads.
## What changed
- Add one registration: `app.slots.experimental_tab({ id, title, icon,
tooltip?, layout?, context, availability, useVisible?, component, ...
})`. Context is `{ kind: "thread" }` with `{ threadId }` props, or `{
kind: "nav-panel", panelId }` with `{ subPath }` props. Fixed tabs have
one non-closable identity; action tabs launch closable instances with
persisted/deduplicated params. Only actions accept `run` and params.
Navigation-page actions also fit this model.
- Use one registry and destination dispatcher.
`ExperimentalAppPanel.experimental_openTab` is the new opener; existing
fixed-tab, action and thread-panel open calls remain wrappers over the
same implementation. A visibility hook hides a fixed tab or launcher
while retaining saved fixed order and existing action instances. Hooks
execute inside the plugin crash boundary; plugin generations reset
crashed content across every context.
- Register core Info and Diff as thread/fixed tabs. Preserve Diff's
Meta+D toggle, changed-file/commit entry points, Git setting, visibility
and pane-local review controllers. Preserve #6117's ordered Info row and
section registrations. There is no core-only Info/Diff content dispatch.
- Migrate first-party Docs, GitHub, Side Chat, Tasks, Workflows and the
ThreadChat demo. Remove the unshipped `experimental_threadFixedTab` API.
Remove `threadPanelAction`, `navPanel.fixedTabs`, and their old
registration/prop types from public SDK declarations and normal
Guide/authoring documentation. Keep lenient runtime adapters so
already-published plugins keep loading. Git install/update and
path/builtin rebuilds emit migration **warnings**, also recorded in
plugin logs; they never fail builds for these retired names. Runtime SDK
exports remain available. Record the 92/15 known legacy marketplace
callers and removal criteria in `docs/api_to_audit.md`.
- Preserve existing `plugin-page-fixed` and `plugin-panel` IDs/params.
Convert old Info/Diff entries, selected IDs and split placement to core
`plugin-fixed` entries without reordering, including revision-checked
server rewrites. SDK/CLI thread tab inspection/update uses the same
persisted model. No SQL schema or daemon wire change.
- Update Plugin Guide cards/surfaces and authoring migration examples.
Rebase onto main `2884950f17` (#6125); bump SDK with
`bump-plugin-sdk.mjs --patch` to **0.6.73**, above main's 0.6.72.
New-thread-screen actions retain their separate project context and
share the action-opening implementation.
- Resolve #6125 conflicts by retaining the retired PR-hook runtime
export and both tab adapters. Keep named-import and
property-registration warnings together, all non-fatal. Use main's
returned `migrationWarnings` and `logPluginBuildWarnings` pipeline for
all three names so warnings are logged once; preserve both audit entries
and migration docs. First-party callers of the new tab API require SDK
>=0.6.73.
## How you verified
- After rebasing, freshly clone/build/install **Graph** at
`3fd202e9af47` and **T3 Sidebar** at `8b535729dd22` through bb's Git
install flow. Both artifact manifests report SDK 0.6.73. Graph logs the
two tab warnings; T3 logs the retired PR-hook warning at
`src/ThreadCard.tsx:6`. Both have zero plugin-log errors. Graph's
thread/fixed tabs render, and T3's actual sidebar renders PR #6013
alongside the matching core Info row, with no browser errors. Restore
the sidebar preference and disable T3 after QA. Evidence:
`/Users/michael/.bb/thread-storage/thr_fkjwifewbd/pr6013-rebase-6125/verification.md`.
- `pnpm exec turbo run typecheck lint test --filter='...[origin/main]'
--concurrency=1` passes: **243/243 tasks**, including 6,323 app tests
and 3,675 server tests. SDK version guard, formatting and `git diff
--check` pass. The unchanged source-layout builtin watcher test failed
in the initial broad run, then passed in its complete isolated suite
(28/28) and the full rerun. No timeout was weakened.
- One six-case contract table covers new thread fixed/action, new
navigation-page fixed/action and both legacy adapters: stable saved
identity, persisted params, deduplication, close behavior, visibility,
remount and crash/reload. Existing migration and split-readiness tests
remain. Independent mutations prove crash recovery failures in both the
table and the actual navigation-page host; repaired focused tests pass.
A full-suite failure also verified the legacy fixed-target callback
contract before its repair.
- Real same-store upgrade from main `c0013e2aba`: save
first-party/marketplace tabs and a Side Chat instance, upgrade to the
branch, and compare persisted data. Every action entry, parameter and
order remains unchanged; Info/Diff convert in place. Verify Info, Diff,
changed-file navigation, Meta+D, Docs/Tasks navigation, GitHub,
Workflows and restored Side Chat. Prior branch evidence covers
reordered/split Diff restoration and the Git setting.
- Actual fresh Git-source install flow at immutable public commits:
**Agent Graph** `3fd202e9af47`, **SQL** `69593e887aaf`, **Host Monitor**
`1d84e329e69d` (`plugins/host-monitor`) and **JB Flow** `66d4700cb1c3`.
They build, load and log the migration warnings. Graph/SQL/JB Flow
action tabs open, close, reopen and restore once; Graph/SQL/Host Monitor
fixed tabs reopen and persist without close controls. No errors in the
fresh app console or their plugin logs. JB Flow's published commit has
actions but no fixed tabs.
- Install original published **Lazygit 0.2.10** and **Global Workflows
0.1.3** npm bundles; app.js hashes match the scanned published
artifacts. Both legacy action tabs open, close and restore. The Lazygit
terminal renders; Global Workflows shows an empty configured catalog. No
workflows, database queries, voice sessions or external GitHub writes
run.
- **Handsfree 0.1.6** has the same sidebar voice-bar crash on main and
this branch; its fixed Views tab renders the same companion state at
phone width. This is recorded as a pre-existing baseline, not counted
toward the three working fixed-tab plugins.
- Review app runs from the new head with `pnpm start:worktree`:
[prepared
thread](https://ymichael--26550.getbb.app/projects/proj_nyijgxmg6f/threads/thr_whvufeb6m5).
Detailed local evidence:
`/Users/michael/.bb/thread-storage/thr_fkjwifewbd/pr6013-unified/verification.md`.
- Size: **80 files, +3,093 / -999**; production/tooling +1,805/-673,
tests/support +1,095/-195, docs +193/-131. The previous cleanup removed
188 net lines, including the JSX glyph assertion and redundant fixed-tab
setup. Visibility observers remain so plugin hooks run within crash
isolation.
> AGENT GENERATED
ymichael
added a commit
that referenced
this pull request
Oct 23, 2026
…PR Status plugin (#6023) ## Human comments ## What was wrong Git and GitHub pull request status were still core UI. The composer's context banner hard-wired its parent-thread, changed-files and pull request rows. The Info tab drew the Pull request row from core, using PR data owned by the bundled GitHub PR Status plugin (#6125). There was no slot through which core and plugins could add context beside a thread's composer. ## What changed - **`app.slots.experimental_threadContextSegment`** (SDK 0.6.75): segments share the context row above an existing thread's composer. - A registration takes separate `summary`, `action?` and `details?` components, like composer customizations take separate lists. The host mounts each in its own crash boundary: `summary` in the row, `action` at the row's end, `details` below the row. - `group` then `order` place segments, as for Info rows and thread actions. `experimental_THREAD_CONTEXT_SEGMENT_GROUPS` names only core's groups, `thread` (`1_thread`) and `git` (`3_git`); the PR plugin uses its own `2_pullRequest`. - Props give `threadId`, `projectId`, `environment`, the visible `segments` as `{ key, pluginId }` (`key` is `<pluginId>/<id>` with core as `bb--core`, the shape thread action keys use), and a shared one-open-at-a-time disclosure. - `useVisible` runs in plugin scope per thread. - The banner is drawn above `ComposerBox`, so it shows under every composer plugin. - **Core uses the same path.** The parent thread (including read-only history) and changed files (with the merge-base action) register through `definePluginApp` and the collector, and render through the same host as plugins. Core segments render without a plugin crash boundary, as core Info rows do, so their DOM is unchanged. - **Active child threads are their own core row** (`ThreadChildThreadsBanner`) above the context row, like the todo and workflow cards, with their own open state. They are not a segment, so the slot has no card placement that would overlap composer `banners`. - **GitHub PR Status plugin gets an app entry** (`plugins/github-pr-status/app.tsx`, `ui/`): - the pull request segment: status pill, label rules, Mark ready and the merge split button with Convert to draft, the remembered merge method under the same localStorage key, and the same toast copy; - the Info tab's Pull request row, through #6117's `experimental_threadInfoRow`; - the display helpers, status pill, next-step label, favicon and the keyed read hook, moved from core with their tests. Core has no pull request UI or PR imports left. The one exception is the retired `experimental_useSidebarThreadPullRequest` runtime member for old marketplace plugins. - **`environment` on slot props.** It is `{ id } | null` on the new slot and on `ExperimentalThreadInfoProps`. - On both, it is null when the thread has no environment, its host is not active, or the environment is not a usable checkout. - On the segment slot only, it is also null when "Show Git changes and Commit button" is off. That keeps main's behaviour, where the PR hides with changed files and the Info row does not. - **No extra PR reads after the move.** - The plugin hook refetches only when an environment starts showing, not for each extra view, so opening Info adds no read. - `WebSocketManager` sends its final unsubscribe a microtask later and cancels it when the same target resubscribes in that task. Without this, a thread switch on the same environment dropped the plugin read subscription and restarted the server's watch, which can run `gh`. - `BannerActionSlot` and the banner action buttons moved to `@bb/shared-ui/prompt-banner-actions`, so the plugin draws the same controls. - **One toast path for core and plugins.** The `toast` plugins import from `sonner` is now a facade over core's `appToast` (`apps/app/src/lib/notifications/plugin-toast.ts`), replacing the recording wrapper. - `toast()`, `message`, `info`, `success`, `warning`, `error`, `loading`, `dismiss` and `promise` render and record exactly like core's toasts. - `custom`, `getHistory` and `getToasts` pass through. - A toast whose `action` or `cancel` is a plain React node (sonner allows it; no bundled plugin uses it) falls back to sonner. - Every plugin's toasts change appearance to match core's, which is intended. - **The retired `experimental_useSidebarThreadPullRequest` member keeps main's refresh.** It uses main's hook, kept in core as `useRetiredSidebarEnvironmentPullRequest` with main's tests: a cached row refetches on mount, and a stale row on page show. - Core's copy and the plugin's copy of that hook announce every refetch they start with a `window` event (`PULL_REQUESTS_REFETCH_EVENT` in the plugin contract) and mark those rows loaded. - A stale-row refetch rechecks staleness right before it runs, so a row that already has a fetch in flight, or was refreshed within its staleness window, is not read twice. SDK refetch behaviour is unchanged. - **Docs:** - a `docs/api_to_audit.md` entry; the Info entry and the live-reads and retired-member text updated; - the Banners Guide card's symbols, bullets and fixture, with anatomy anchors; - `frontend-registration.md` and `frontend-api-index.md`; - the PR stories moved to the plugin and added to Ladle. No database, daemon wire or CLI changes. Rebased onto #6143. Its "hide cached PR status when the plugin is disabled" check in core's PR hook moved with that hook's last core caller, the retired sidebar shim (`retired-sidebar-pull-request.ts`), with #6143's test; the plugin's own hook only renders while the plugin is enabled. The keyed read stays one PR per environment, so the segment and Info row need no change. **Deviations from main** - While core's commit or push runs, the PR buttons are no longer disabled; the plugin cannot see core's git actions. The replacement Michael asked for is to pass the displayed head to `gh pr merge --match-head-commit`. That needs `headRefOid` on the PR, which #6143 adds, so it lands on top of #6143. - A crash in the PR segment removes only that segment, or its part. - The active child threads card keeps its own open state, as Michael asked. On main it shared one open section with the context row. Now it can be open while changed files or the parent details are open, and it opens by itself when a child newly needs input even while another section is open. **Known follow-ups:** - The segment slot's `environment` folds in the "Show Git changes and Commit button" setting. That setting should become the git plugin's own when changed files move out of core, and the `git` segment group moves with it. - `experimental_THREAD_INFO_ROW_GROUPS.pullRequest` (from #6117) still names a plugin's group in core. Removing it is a public SDK removal, so it is left for a separate change. ## How you verified - Parity checklist: every banner row and segment, the Info PR row, refresh sources, phone and desktop layout, and each composer. Item IDs: A composers, B inline row, C child card, D read-only, E disclosure, F data, G layout, H Info row. <details><summary>Checklist results</summary> | Checklist | Result | |---|---| | A1, A2, A3, A8 | Same. A4–A7 unchanged code paths (banner only in the follow-up stack); not driven separately. | | B1–B5, B11, B13–B15 | Same text and pixels at 1400 and 390 px for: PR + changed files, PR alone, parent + PR + changed files, parent + PR. | | B6–B8, B10 | Same: menu items, Convert to draft, Mark ready, Squash merge on disposable PRs; toasts identical. | | B7 persistence | Same key `bb.pullRequest.mergeMethod`; Squash merge remembered. | | B9 | Deviation: core commit/push no longer disables the PR buttons. | | C1–C6 | Same content, auto-open on a new waiting question and 8 px spacing; the card is now its own row with its own open state (deviation above). | | D1–D3 | Same pixels for archived child with parent. | | E1–E4 | Same: parent disclosure body, focus to Collapse, focus back to toggle. | | F1–F6 | Same PR reads (4 per scripted session); fewer WS frames on the branch. | | G1–G4 | Same compact rules (390 px and narrowed by the right panel). | | H1–H4 | Same markup and row order; Show Git changes off keeps the row on both; plugin disabled hides it on both. | </details> Each item was driven in two source dev instances side by side, one on this branch and one on `origin/main` 2884950, with identical fixtures and disposable PRs in `ymichael/test-project`. - Text was identical at 1400 and 390 px for PR + changed files, PR alone, parent + PR + changed files, and parent + PR. - Screenshots were pixel-identical apart from the caret and the scheduled-time text. - The same held for archived history, Show Git changes off, the plugin disabled, the child-thread card with a waiting question, the pending-question stack, and the parent disclosure with its focus handoff. - Toasts: the PR plugin's Mark ready toasts match main's pixel for pixel; the loading toast differs only in the spinner frame. The Drafts plugin's "Draft saved" and the probe's `success`, `error`, `info` and `promise` toasts render as core toasts, with the close button, the outlined icon and an entry in the notification center. - Actions on disposable PRs: Convert to draft, Mark ready and Squash merge went through with the same toasts and the segment refreshed. The merge method was remembered. A CLI `bb pull-request draft` updated the segment live in the production build (`pnpm start:worktree`). - Requests, from browser CDP counts of `bb--github-pr-status` HTTP reads and WebSocket frames. The scripted session: load a thread with a PR and wait 40 s, open Info, switch to a thread on the same environment and back, switch to another environment and back, then fire a page show after 35 s. | | PR reads | WS frames | |---|---|---| | main (dev) | 4 | 12 | | branch (dev) | 4 | 8 | | branch (production build) | 4 | 8 | Before the WebSocket fix the branch sent 16 frames, because of resubscribes on same-environment switches. With a probe plugin that renders `experimental_useSidebarThreadPullRequest` in the thread header (an old marketplace sidebar's call), the same session gave: | | load | 4 thread switches | page show | |---|---|---|---| | main | 1 | 4 | 1 | | branch | 1 | 4 | 1 | Before the shared announcement, the branch's page show read twice. - Tests: - new host contract tests (`ThreadPromptContextBanner.segments.test.tsx`): group and order placement, a plugin's own group, `segments` keys, visibility, one-open disclosure, per-component and `useVisible` crash isolation, read-only, and the row held while git loads; - child-threads row tests: auto-open for a newly waiting child while changed files are open, and the question flow through `ThreadChildThreadsBanner`; - plugin tests for label rules, the attention matrix, actions and visibility; - query tests for refetch on first show and same-commit remounts, which fail without the deferral; - a WebSocketManager test for same-task resubscribe; - a plugin `toast` test through the real toaster (error and promise), which fails on the old shim; - a test with the plugin's hook and the retired member's hook on one environment: a stale page show makes one read, and two without the announcement. - `pnpm exec turbo run test typecheck lint --filter='...[origin/main]' --continue`: 239/244 passed on the first run. - Fixed: the Automations Info fixture (`environment`), the server's slot-prop docs table, and the 500-line cap on `frontend-registration.md`. - Passed when rerun alone: `CommandPalette`, `PluginSettings`, `builtin-plugins` hot-reload and the MCP CLI timeout (the first run had heavy parallel load). - Affected packages rerun green: `bb-plugin-plugin-api-docs`, `bb-plugin-automations`, `@bb/server`. - `bb plugin build` passes for `plugins/plugin-api-docs` and `plugins/github-pr-status`. The Plugin Guide's Banners card and fixture were checked in the app at desktop and phone layouts. - After the groups and component split: `pnpm exec turbo run test typecheck lint --filter='...[origin/main]' --continue` gave 242/244. A test-harness mock of the banner and the old auto-open test were updated and `@bb/app` reran green; `bb-plugin-bb--storage-retention` (untouched) passed when rerun alone. - In the production build (`pnpm start:worktree`) on a checkout of this branch: parent, PR and changed files in that order with Merge at the end; the child-threads row above the context row, open together with changed files; only the child row beside a waiting question. - CI: see the checks on this PR. > AGENT GENERATED 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 23, 2026
Closed
VKirill
pushed a commit
to VKirill/bb
that referenced
this pull request
Oct 25, 2026
… bb's Info section slot desktop-v0.47 ships experimental_threadInfoSection (get-bb#6117). The VK slot now registers the section there: group changes, order 30 (after commits and uncommitted changes, before storage), inside the labelled group it always had. bb renders it: per-pane mounts, ordering, crash isolation. The app-side hook points are gone (VkPluginThreadInfoSections, the plugin-slots kind, the ThreadMetadataContent registration, the harness field). Lane Pilot keeps working unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 28, 2026
VKirill
pushed a commit
to VKirill/bb
that referenced
this pull request
Oct 29, 2026
… bb's Info section slot desktop-v0.47 ships experimental_threadInfoSection (get-bb#6117). The VK slot now registers the section there: group changes, order 30 (after commits and uncommitted changes, before storage), inside the labelled group it always had. bb renders it: per-pane mounts, ordering, crash isolation. The app-side hook points are gone (VkPluginThreadInfoSections, the plugin-slots kind, the ThreadMetadataContent registration, the harness field). Lane Pilot keeps working unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VKirill
pushed a commit
to VKirill/bb
that referenced
this pull request
Oct 30, 2026
… bb's Info section slot desktop-v0.47 ships experimental_threadInfoSection (get-bb#6117). The VK slot now registers the section there: group changes, order 30 (after commits and uncommitted changes, before storage), inside the labelled group it always had. bb renders it: per-pane mounts, ordering, crash isolation. The app-side hook points are gone (VkPluginThreadInfoSections, the plugin-slots kind, the ThreadMetadataContent registration, the harness field). Lane Pilot keeps working unchanged. Co-Authored-By: Claude Opus 5.5 <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.
Human comments
What was wrong
The thread Info tab was a hard-coded list of core rows and sections in
ThreadMetadataContent, so plugins had no way to add to it. Automations that target a thread (#5554) could only be seen in the global Automations view. An earlier attempt (local commit 11ec452c81) gave plugins a data hook that only core rendered from, and core did not use it itself.What changed
Two slots, used by core and plugins alike
app.slots.experimental_threadInfoRowadds a row to the details block, andapp.slots.experimental_threadInfoSectionadds a section below it.{ id, group, order?, component }. The component receives{ threadId, projectId }and mounts once per visible thread.ThreadMetadataContentnow renders only throughThreadInfoRows/ThreadInfoSections(apps/app/src/components/secondary-panel/info/ThreadInfoSlots.tsx). These mergeCORE_THREAD_INFO_ROWSandCORE_THREAD_INFO_SECTIONSwith plugin registrations.group, thenorderwithin it (unset last), then registration order, with core first on a tie. The comparator thread actions used moved toapps/app/src/lib/grouped-placement.ts, and both use it.experimental_THREAD_INFO_ROW_GROUPS(thread, environment, git, pullRequest, lifecycle) andexperimental_THREAD_INFO_SECTION_GROUPS(related, changes, storage). Core uses orders 10, 20, 30 within each group and keeps today's order.Shared building blocks
experimental_InfoSection(heading, count pill, collapse),experimental_DetailRow,experimental_InfoList(the "N more" list) andexperimental_InfoListRoware the components core draws with.DetailRow({ label, icon?, align?, children }) is now bb's only label/value row: every core Info row and the commit dialog draw with it, and it isexperimental_DetailRow. The old variant with class overrides is deleted. Branch and Archived truncate through an inner span, and the commit dialog's vertical changed-files row draws its own markup.commits,forks, …), so saved collapse state carries over. Plugin sections are stored as<pluginId>/<id>ininfoPanel.collapsedSections.divide-yon direct children to each[data-info-section], so plugin wrappers do not break them.Automations (first plugin caller, #5554)
relatedgroup, ahead of Child threads, lists automations whoseexecution.targetThreadIdis the open thread. It shows 3 rows with "N more", status labels from the overview, and refreshes on any automations realtime signal. Selecting a row opens the automation.automations_listandbb automation list --target-thread <id>take the target-thread filter. This is the server, CLI and test part of 11ec452c81. The skill documents it.Docs and API process
docs/api_to_audit.mdentry with core callers and audit items.Not moved through the slots
hasAnyThreadMetadataplus the forks query inThreadDetailSecondaryContent) still decides from core state whether the tab shows details, a skeleton, or "No thread details available." Plugin entries do not count toward it. This is audit item 3.No server/daemon wire changes.
How you verified
Tests
ThreadInfoSlots.test.tsxcovers:thread-info-section.test.tsx(automations) covers the target-thread RPC, realtime refresh from another project, status labels, and navigation.automations_listand the CLI--target-threadfilter.turbo run test typecheck lintfor@get-bb/plugin-sdk,@bb/app,bb-plugin-plugin-api-docs,bb-plugin-automationsand@bb/domain: 20/20 tasks.turbo run typecheck lint --filter='...[origin/main]': 142/142 tasks.@bb/servertest/services/plugins: 551 tests passed.check-plugin-sdk-version.mjspasses.DetailRowconsolidation: Info panels at 589 and 339 px wide (long branch names truncating, an archived thread, a PR) and the commit dialog were pixel-compared against main. The dialog and five of eight panels are identical; the rest differ only by an animated glyph, a ticking relative time, and one anti-aliased pixel.Parity with main in the real app
apps/appat main (cc292b9) and at this branch, and served each through a proxy in front of the live bb, so both builds showed the same real threads.Automations in a dev instance (
pnpm start:worktree)/plugins/automations/automations/<project>/<id>.Fixes #5554
🤖 Generated with Claude Code