Repository navigation
Bots: Bots-only sidebar and the Bot side panel (bot chat activity C2) - #478
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tention Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e shortcut Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…the last view on failure Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The panel sits on the C1 workbench shell beside a Bot's chat, under its own id, surface name, width key, focus hand-off and Escape. A file request lands only on its own Bot and only once, even across a remount; it is dropped only when the chat reaches a different Bot, so a hop through a non-chat page keeps a request made for the next chat. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…log is removed Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Activity says when the conversation couldn't be read, with Try again, and owns its live read so an event re-renders that tab alone. Files keeps the list and the open file on screen while they are read again, shows a failed refresh above them, and Try again re-reads only what failed. The Activity log and the in-chat recap share one step row; the Bot panel hides the workbench's Open tabs list; headings sit at one level in every tab. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd unread On any Bots page the sidebar lists only Bots: back to Aiden, Search and New Bot; the pinned Bot large on top; one row per Bot, newest activity first, with a working face, "Thinking…"/"Waiting for you", Needs you and an unread dot; Bots on paired Macs (filtered by search); Profile and Connect apps. - Rows re-read one Bot on bots:attention; an older summary answer that arrives late never replaces a newer one. Prune runs only on a settled, successful list read; deleting a Bot forgets its pin and view, even the last Bot. - The chat marks its Bot viewed on open, on each arriving message and on leaving; the chat's "All Bots" back button is gone. - The Bot menu's focus guard is per menu and set only after the action ran; a row's right-click menu hands focus back to the row when the panel closes. - The old list's duplicate row menu uses the shared Bot menu; store-level togglePin/forget replace pin/unpin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…avigation
- bots:changed with { botId, session } re-reads only that Bot's summary; any
other change naming a Bot re-reads it and the list; an unnamed change
re-reads everything. bots:attention is handled once per window in the same
sync, so two row readers never double the reads.
- Bot tests load TanStack Router's client behaviour (NODE_ENV=test before it
is imported), so navigations mount the routed page; Memory from a row,
leaving a chat and click-then-back now prove real navigation.
- Empty states count paired Macs' Bots; search ignores accents and case and
reads a bounded preview; a row's preview is its description, bounded.
- The footer's Profile name starts with the visible name; rows are memoised;
Shift+F10 / Menu opens a row's menu; deleting a row's Bot leaves focus on
the nearest row or New Bot; a deleted Bot is never marked viewed; a failed
refresh keeps the list with Try again; the pinned Bot shows a pin mark.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…emory /bots shows a skeleton while it waits and falls back to bot.updatedAt if a live summary never answers. Tests for the create-flow suppression and a stale cached list. The Bots onboarding tile's header now shows the panel toggle in place of a back chevron. Spec rulings and deviations, plan index and project memory record what C2 shipped. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e view stay put - Unread compares a Bot's last reply with the newest reply its chat showed (seenReplyAt, main's clock), so a reply that started before the person left and finished after is unread. lastViewed only picks the Bot /bots opens. - Deleting a Bot that is not on screen no longer leaves the current page. - Files hands focus to the open file's heading, and back to its row on All files, so Escape still closes the panel. - A consumer joining a failed live store keeps the last view while it retries. - Search folds only Latin accents; Devanagari or Thai marks still count. - Delete focus follows the on-screen order, pinned Bot first; the pinned Bot describes its working state; the Activity scroller drops its duplicate name. - One turn walker (bot-turns.ts) feeds both the chat rows and the Activity log. - The Activity log skips streamed tokens; the chat reads only its own pin; menu actions read the open chat at choice time, so rows keep their callbacks. - Dead surface removed (useSharedBotLive, BotLiveProvider, botLiveSummaryKey, RemoteBotsSection's query) and local-only names un-exported. - The test harness fails loudly on the router's server build, mounts inside act, and the Bot suites wrap navigations and notifications in act. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… clear button - The Bot panel has its own width range (400–720, default 440), so at a 1280px window it reflows the chat instead of covering the composer. It floats only where the workspace panel would float too. WorkbenchPanel and the layout helpers take optional width bounds; the workspace's are unchanged. - The Bot ••• menu has leading icons (MemoryCardIcon for Memory). - The unread dot sits after the name and "Needs you" leads the second line, so every row's name and preview line up. - The search field is a text input in the searchbox role, so WebKit adds no second clear button. - e2e at 1280×800: the composer stays clear of the panel, the workspace surface is inert and unnamed while covered, and bots:attention reaches a row through the app's own notification sync. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The spec and memory describe the seen-reply watermark, the Bot panel's width range, the shared turn walker and the "All Bots" exceptions (Profile's loading and not-found shells, the chat's not-found shell); mark the pre-C2 evidence as "(was)"; and keep every parked item from the ledger. Previews are re-read per Bot on bots:changed and bots:attention. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hermes Review BotConfidence: 5 Engine: SummarySlice C2 replaces the former Confidence Score: 5/5Fully traced and verified across all changed files, main IPC handlers, renderer state stores, preference persistence, focus management, and e2e test specifications. 📁 Important Files Changed
FindingsNo findings. Sequence DiagramsequenceDiagram
autonumber
participant Main as Main (Bot Runtime)
participant Sync as Renderer (BotNotificationsSync)
participant Query as TanStack Query Cache
participant Sidebar as BotSidebar
participant Chat as BotChatPane
participant Panel as BotWorkbench (BotPanel)
Main->>Sync: notify("bots:attention", { botId })
Sync->>Query: invalidateQueries(["bot-live-summary", botId])
Query->>Main: invoke("bots:live:summary", botId)
Main-->>Query: BotLiveSummary (needsYou, lastReplyAt)
Query-->>Sidebar: re-render row (shows "Needs you" / Unread dot)
Sidebar->>Chat: navigate to /bots/$botId/chat
Chat->>Main: markViewed(botId, replyAt)
Chat->>Panel: open panel tab (Activity / Files / Memory / Routines)
Panel->>Chat: reuse shared BotLiveStore (single live subscription)
[]
|
There was a problem hiding this comment.
Important
Three reproducible regressions need attention before merging: same-file chips can show stale contents, same-route sidebar actions can do nothing, and hiding the panel can leave its Memory menu interactive.
Reviewed changes: Reviewed the complete C2 diff against its stacked C1 base, including the desktop runtime seams, renderer behavior, tests, and mobile contract implications.
- Bots Sidebar: Replaces the workspace sidebar on Bots pages with pinned and activity-ordered Bots, search, attention/unread indicators, shared menus, and paired-Mac rows.
- Bot Panel: Adds Activity, Files, Memory, and Routines on the shared workbench shell, with independent state, width, focus handling, and route-aware shortcuts.
- Live State And Navigation: Shares one live subscription per Bot, refreshes summaries per notification, extracts the shared turn walker, and redirects
/botsto the most recently used Bot. - Coverage And Onboarding: Adds behavioral renderer/runtime tests and Electron e2e coverage, corrects the client-mode router harness, and updates the onboarding illustration.
Validation: TypeScript, 220 focused tests, and test:aiden-remote passed; three additional diagnostic probes reproduced the inline findings. Android verification was blocked by an unavailable offline Gradle plugin and an online retry reporting Tool 'shell' timed out after 120000ms. Native iOS and Electron e2e were not run on this Linux runner; hosted CI is not yet green.
gpt-6.1-sol | 𝕏
| React.useLayoutEffect(() => { | ||
| if (pendingKey === null || pendingPath === null) return; | ||
| handledRequestKeys.set(botId, pendingKey); | ||
| setPath(pendingPath); |
There was a problem hiding this comment.
[P2] Refresh a newly requested file even when its path is already selected. If the Bot edits the file currently visible in Files, clicking the new edit chip leaves the previous contents on screen: this sets the same path, so no second bots:files:read runs. The new side-panel interaction makes this possible without closing the file viewer first.
Technical details
# Same-path file requests leave stale contents
## Affected sites
- `renderer/main/bots/bot-panel-files.tsx:80-84`: a new scoped request is marked handled but only updates `path`.
- `renderer/main/bots/bot-panel-files.tsx:108-134`: the document read depends on `active`, `botId`, `path`, and `documentAttempt`, not a newly applied request.
- `renderer/main/bots/bot-chat-pane.tsx:836`: every chip creates a new request, including later edits of the same file.
## Reproduction
1. Open `plan.md` from a chat chip and leave Files selected.
2. Have the Bot edit `plan.md`, producing another file entry/chip.
3. Click the new chip while the existing viewer remains open.
4. The viewer still shows the old document and the read count remains one. A diagnostic probe using the real chat/workbench and a mutable file-read fixture reproduced this.
## Required outcome
- Each newly applied file request must refresh its document even when its path is unchanged, while preserving the existing Bot scoping and refresh-error behavior.
- Extend the behavioral panel suite with a same-path edit whose returned contents differ; assert that clicking the new chip shows the new contents.| (bot: BotDefinition, action: BotMenuAction, returnFocus?: HTMLElement | null) => { | ||
| switch (action) { | ||
| case "profile": | ||
| void navigate({ to: "/bots/$botId", params: { botId: bot.id } }); |
There was a problem hiding this comment.
[P2] Make same-route menu actions change the displayed subpage. Open a Bot's Profile at /bots/$botId, enter Instructions using its row, then choose Profile from that Bot's sidebar menu: Instructions stays visible because this navigation targets the unchanged URL. Advanced has the equivalent problem after a local subpage transition while the URL still has ?page=advanced.
Technical details
# Sidebar actions can be no-ops on the current Bot page
## Affected sites
- `renderer/main/bots/bot-menu.tsx:228-232`: Profile and Advanced act only through URL navigation.
- `renderer/main/bots-view.tsx:108-112`: displayed `page` is local state; it synchronizes only when `botId` or parsed `initialPage` changes.
- `renderer/main/bots-view.tsx:159-161`: Instructions, Advanced, and Memory can be entered locally without updating the URL.
## Reproduction
- Mount the actual `BotsView` and `BotSidebar` at `/bots/bot-1` with the client-mode router.
- Click Instructions, then choose Profile in the sidebar row's menu for Bot 1.
- After navigation settles, the URL remains `/bots/bot-1` and `Instructions for Planner` is still rendered. This reproduces without making any unsaved edits.
## Required outcome
- An explicit Profile or Advanced menu choice must select that surface even if its requested URL already matches the current route.
- Keep the displayed subpage and external navigation actions coordinated, and cover this with the actual routed page rather than a navigation-only fixture.|
|
||
| const close = React.useCallback(() => { | ||
| const active = document.activeElement; | ||
| if (active instanceof HTMLElement && active.closest(`#${BOT_PANEL_ID}`)) { |
There was a problem hiding this comment.
[P2] Include panel-owned portals in the close lifecycle. With Memory options open, environment.toggle closes and makes the panel inert, but its portalled menu remains open and interactive because focus is outside #bot-panel. Selecting Edit then opens a Memory dialog for the hidden panel instead of dismissing the panel's controls.
Technical details
# A hidden Bot panel leaves its Memory menu active
## Affected sites
- `renderer/main/bots/bot-panel-context.tsx:132-145`: close detects focus only through DOM ancestry and does not dismiss panel-owned portals.
- `renderer/main/root-view.tsx:148-150`: the environment command invokes the panel toggle while a dropdown is open.
- `renderer/main/bots/bot-memory-page.tsx:180-191`: Memory options uses the shared portalled DropdownMenu.
- `renderer/components/ui.tsx:1639-1652`: DropdownMenuContent renders through a Radix Portal.
## Reproduction
1. Open the Bot panel on Memory and focus/open an entry's Memory options menu.
2. Invoke `environment.toggle` while focus is on the portalled Edit item.
3. The panel becomes closed/inert, but Edit and Delete remain available outside it.
4. Selecting Edit opens the Edit memory dialog. A diagnostic probe reproduced this through CommandSystemProvider's `app:command` notification and the actual panel actions/content.
## Required outcome
- Hiding the panel must also dismiss its active owned overlays and restore focus to a visible non-inert target, or defer the toggle while the overlay owns the interaction.
- Add behavioral coverage for the command while a panel-child menu is focused, not only for Escape inside the panel DOM.
## Framework reference
- Radix menu content uses portal/focus-scope handling: https://github.com/radix-ui/primitives/blob/main/packages/react/menu/src/menu.tsx
Slice C2 of Bot chat activity. It stacks on #476 (C1, the workbench shell), which stacks on #477 (B) and #475 (A). Plan:
docs/superpowers/plans/2026-10-14-bot-chat-activity-c2-sidebar-panel.md.What changes for people
The sidebar on Bots pages lists only Bots. It works like Grok's:
The Bot side panel is built on C1's
WorkbenchPanel, with four tabs:It sits beside the chat, and floats only where the workspace panel would also float. The environment shortcut toggles it on a Bot's chat. On Bots pages the workspace surfaces, Quick View and the mini player are hidden, with their state kept.
Opening the panel. File chips, ••• Files and "Memory updated" now open the panel.
One ••• menu everywhere: Profile, Files, Memory, Routines, Advanced, Pin to top, then Delete Bot. Each item has an icon.
/botsopens your most recently used Bot. The list page is gone. With no Bots you see the starter page.Onboarding: the Bots feature tile shows the side-panel toggle.
Under the hood
bots:live:summarygainsneedsYouandlastReplyAt.bots:attentionsignal carries{ botId }, through an electron-freewireBotAttentionseam.bots:changedwith abotIdnow re-reads only that Bot's summary.test:aiden-remotepasses, andBotLiveSummaryhas no Remote consumer.use-bot-live.ts) is shared by the chat and the panel. It survives same-commit remounts.bot-turns.ts) feeds both the chat recap and the Activity log.WorkbenchPanelgains optionalsurfaceandtabMenuprops and per-panel width bounds. The defaults keep the workspace exactly as it was; theworkspace-panelandenvironment-focuse2e tests pass.NODE_ENV=test(renderer/main/bots/test-env.ts), so TanStack Router behaves as a client and navigation tests are real. A guard throws if the router ever falls back to server mode.Rulings and deviations
These are recorded in spec §8/§17 and
.memory/bot-chat-activity.md:/botswaits up to 800 ms for summaries, then falls back toupdatedAt.Known, parked
These are listed in spec §17:
Test plan
tsc --noEmit,lint,test:ci-policytest:bots(805),test:bot-runtime(188),test:sidebar(94),test:aiden-remote(920),test:onboarding,test:remote-chat(150)npm test--fail-on-flaky-tests:bots-sidebar-panel(new),bots-starter,guided-setup,workspace-panel,environment-focusProcess
Built by subagents task by task. Each task got its own review and fix rounds, then an Opus whole-branch review, one final fix wave, and a re-review with every finding addressed.
CI notes
Run 38570083858 had two failures, neither in files this PR touches:
scripts/worktree-file-io.test.mjs"editor operations reject a symlinked ancestor…" hit an uncaughtwrite EPIPE, becauserunEditorcallschild.stdin.endand the editor exits first. It is the same race seen on Bot chat activity 3/5: Extract a reusable workbench shell from the environment panel #476 and is still unfixed on main.tests/e2e/chat-message-queue.spec.ts:481"a response finishing while the queue editor is open waits for the saved edit" flaked. The first attempt never saw "Saved after completion" within 10 s, and retry Use catalog-driven runtime model limits #1 passed.--fail-on-flaky-testsfailed the job, as intended. (job)I reran the whole run once. No timeouts were raised and no retries were added.
🤖 Generated with Claude Code