Skip to content

🤖 feat: merge Costs + Stats into unified Stats tab with sub-tabs - #2729

Merged
ibetitsmike merged 12 commits into
mainfrom
mike/tabs-layout-et3t
Mar 3, 2026
Merged

ibetitsmike merged 12 commits into
mainfrom
mike/tabs-layout-et3t

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Mar 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Merge the standalone "Costs" and "Stats" top-level tabs into a single unified "Stats" tab with sub-tabs (Cost, Timing, Models).

Background

Previously the right sidebar had separate "Costs" and "Stats" tabs. The Stats tab was feature-flagged and duplicated some navigation patterns. This consolidates them into a single top-level "Stats" tab with a ToggleGroup for switching between sub-views.

Implementation

  • Internal key preserved: The stored tab key remains "costs" to avoid persisted layout migrations. Only the display name changes to "Stats".
  • Sub-tab container (StatsContainer.tsx): Uses ToggleGroup with "Cost", "Timing", and "Models" options. Timing/Models only appear when the statsTab feature flag is enabled.
  • Extracted panels: TimingPanel and ModelBreakdownPanel extracted from StatsTab.tsx via shared useStatsData hook.
  • Unified tab label: Merged CostsTabLabel + StatsTabLabel into one component showing both cost and duration badges.
  • Layout migration: stripLegacyStatsTab runs as a pre-parse step in parseRightSidebarLayoutState, stripping "stats" from persisted layouts before isTabType validation. This preserves user layout configuration during upgrade.

Generated with mux • Model: anthropic:claude-opus-4-6 • Thinking: xhigh • Cost: $6.61

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 12cf75a7c1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/features/RightSidebar/RightSidebar.tsx Outdated
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5d8ab8f94

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/features/RightSidebar/RightSidebar.tsx Outdated
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

Fixed the migration issue: stripLegacyStatsTab now runs during parseRightSidebarLayoutState pre-parse step, before isTabType validation. Added 3 tests covering simple, activeTab fallback, and split layout scenarios.

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

The migration issue raised was already addressed in the latest commit — stripLegacyStatsTab now runs as a pre-parse step inside parseRightSidebarLayoutState (before isTabType validation), preserving the user's custom layout. Added tests for simple, split, and activeTab-fallback scenarios.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1258f21cf1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/utils/rightSidebarLayout.ts
Comment thread src/browser/types/rightSidebar.ts
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ce28f53186

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/utils/rightSidebarLayout.ts Outdated
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

Addressed both P1 and P2 feedback:

  • P1: stripLegacyStatsTab now ensures at least one tab remains — a stats-only tabset becomes ["costs"].
  • P2: resolvePresetTab maps legacy "stats" → "costs", and resolvePresetNodeToLayout deduplicates tabs to prevent double entries.
  • Storybook: Fixed StatsTabStreaming play function — uses getByTestId("timing-section") instead of getByText(/timing/i) to avoid ambiguity with the sub-tab toggle button.

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

Resolved the new P1 thread — the empty tabset guard was already implemented in the latest commit. stripLegacyStatsTab falls back to ["costs"] when filtered tabs are empty.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

Fixed E2E test failures — all specs referencing the old "Costs" tab name have been updated to "Stats". Also adjusted split layout tab count expectation (4 → 3) since the standalone stats tab no longer exists.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9631afbfad

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/utils/rightSidebarLayout.ts Outdated
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

Addressed the P2 feedback: stripLegacyStatsTab now maps activeTab: "stats" to "costs" when it's present in the tabs array, rather than falling back to the first tab. Added a test for the reordered layout case. Also fixed E2E tests for the Costs → Stats rename.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f12ecb7c6b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/types/rightSidebar.ts
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

Addressed the archive cleanup issue: archiveWorkspace now uses parseRightSidebarLayoutState (which runs stripLegacyStatsTab) instead of isRightSidebarLayoutState directly. This ensures terminal tab cleanup works even for layouts with legacy "stats" tabs.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

Fixed the unit test failure for archiveWorkspace — parseRightSidebarLayoutState auto-injects "explorer" when missing, so the test layout and expected results needed updating.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b4fc81f50

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/features/RightSidebar/RightSidebar.tsx Outdated
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

Added ErrorBoundary back around StatsContainer — restores the fault isolation the old standalone StatsTab panel had. Also fixed the archive workspace unit test.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@ibetitsmike
ibetitsmike added this pull request to the merge queue Mar 2, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Mar 3, 2026
Restructure the bottom panel tabs:
- Rename top-level 'Costs' tab to 'Stats' (internal key remains 'costs')
- Add sub-tabs inside Stats: Cost (always shown), Timing (feature-flagged), Models (feature-flagged)
- Remove standalone 'Stats' top-level tab (absorbed into sub-tabs)
- The statsTab feature flag now controls Timing/Models sub-tab visibility

New files:
- StatsContainer.tsx: Sub-tab container with ToggleGroup switching

Refactored:
- StatsTab.tsx: Extract useStatsData hook, TimingPanel, ModelBreakdownPanel
- TabLabels.tsx: Merge CostsTabLabel + StatsTabLabel into unified StatsTabLabel
- registry.ts: Remove 'stats' entry, rename costs name to 'Stats'
- RightSidebar.tsx: Use StatsContainer, remove statsTabEnabled prop/effect
- Migration: Clean up persisted 'stats' tabs from layouts
- Remove test expecting standalone 'stats' tab (absorbed into 'costs')
- Add assertion that no standalone stats tab exists
- Update renderReviewPanel selectTab types to remove 'stats'
Move stats tab migration to parseRightSidebarLayoutState's pre-parse
step (stripLegacyStatsTab) so it runs before isTabType validation.
This preserves user's custom sidebar configuration instead of resetting
to defaults when persisted layouts contain the old 'stats' tab.

Add 3 tests verifying the migration handles simple, activeTab fallback,
and split layout scenarios.
- Change tab name matchers from /costs/ to /stats/ in play functions
- Set statsContainer:subTab to 'cost' in cost-focused stories to ensure
  the Cost sub-tab is active (prevents timing sub-tab leaking from
  earlier stories via shared localStorage)
…ybook test

P1: stripLegacyStatsTab now ensures at least one tab remains when
    a tabset only contained 'stats' (falls back to ['costs']).

P2: resolvePresetTab maps legacy 'stats' → 'costs' so older layout
    presets resolve correctly. resolvePresetNodeToLayout deduplicates
    tabs to prevent double 'costs' entries.

Storybook: StatsTabStreaming play function uses getByTestId instead
of getByText(/timing/i) to avoid ambiguity with the sub-tab toggle
button.
All E2E specs (sidebarDragDrop, review, reviewRefresh, terminal)
referenced the old 'Costs' tab name. Updated to 'Stats' and
adjusted split layout tab count (4 → 3) since standalone stats
tab no longer exists.
When the persisted activeTab was 'stats' and tabs were reordered
(e.g., ['review', 'costs', 'stats']), prefer mapping to 'costs'
(semantic replacement) rather than the first tab ('review').
Added test for reordered layout case.
archiveWorkspace used isRightSidebarLayoutState directly on raw
persisted data. Layouts with legacy 'stats' tabs would fail
validation, skipping terminal tab cleanup on archive.
Now uses parseRightSidebarLayoutState which runs stripLegacyStatsTab
first.
parseRightSidebarLayoutState auto-injects 'explorer' when missing,
so the test layout and expectations needed updating.
Restores the ErrorBoundary that the old standalone StatsTab panel had.
Prevents TimingPanel/ModelBreakdownPanel errors from unmounting the
entire sidebar.
@ibetitsmike
ibetitsmike force-pushed the mike/tabs-layout-et3t branch from ebff9b0 to e16eb4b Compare March 3, 2026 06:07
@ibetitsmike
ibetitsmike added this pull request to the merge queue Mar 3, 2026
Merged via the queue into main with commit 91eb5d0 Mar 3, 2026
23 checks passed
@ibetitsmike
ibetitsmike deleted the mike/tabs-layout-et3t branch March 3, 2026 07:04
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Sep 25, 2026
…orts (coder#4472)

## Summary

Deletes browser production code that only tests still called, and moves
the tests that guarded live behavior onto the live code path. Part of
the test-audit follow-up campaign (frontend area, WS7).

## What was removed

| Removed | Why it was dead | What happened to its tests |
| --- | --- | --- |
| `StatsTab` component + its test-only `_snapshot`/`_clearStats` props
and `useStatsData` overrides | No production caller since coder#2729 moved
the Stats tab to `StatsContainer` (`TimingPanel` +
`ModelBreakdownPanel`) | `StatsTab.clear.test.tsx` replaced by
`TimingPanel.clear.test.tsx`, which drives the live path: real
`WorkspaceStore` stats subscription + `APIProvider` client whose
`workspace.stats.clear` rejects |
| `compareRecords`, `compareArrays` (`useStableReference.ts`) | Only
`compareMaps` is used (`App.tsx`) | 12 comparator tests deleted; stale
"tested manually via useUnreadTracking…" comments fixed |
| `closeSplit` (`rightSidebarLayout.ts`) | Never called in production
since coder#1340 | Test deleted |
| `isActionableTaskExecutionStatus` | Last caller removed in coder#4341 | No
tests |
| `shouldRefreshInlineSkillSuggestions` | coder#4013 made inline suggestions
synchronously derived, so there is no refresh gate | Tests deleted |
| `getAnthropicThinkingDisableReason` + `hasAnthropicThinkingSignature`
| coder#1450 stopped disabling Anthropic thinking | Tests deleted; the one
transform test that used it as an oracle now asserts the preserved
signature directly |
| `useProjectGitStatuses` (`GitStatusStore.ts`) | No production caller |
None in CI |
| `buildReviewDiffPathFilter`, `normalizeReviewPanelAssistedHunks`
(test-only wrappers in `ReviewPanel.tsx`) | Production calls
`buildReviewDiffPathFilterSpecs` / `getReviewPanelPathContext` +
`normalizeAssistedReviewHunks` directly | Tests retargeted to those
production functions (`getReviewPanelPathContext` is now exported) |
| `computeTaskAwaitPollGroupInfos` (test-only wrapper) | ChatPane calls
`computeOperationalBundleInfos({ taskAwaitPollsOnly })` | Tests use a
local fixture helper around the production function |
| 2 `/fork` cases in `parser.test.ts` | Same contract and name as
`fork.test.ts` | Deleted |

Retained on purpose (test reset seams, not dead code): `__resetForTests`
(highlight worker), `resetAiSelectionIntentForTests`,
`overlayWorkspaceStoreRaw` (test-support module).

## Validation

- Sweep: every `export` under `src/browser` (excluding stories) checked
for non-test references across `src/`, `tests/`, `vscode/`, `scripts/`,
`.storybook/`; orphan-module sweep found only the two HTML entry points.
- `TimingPanel.clear.test.tsx` fails when `handleClearStats` stops
setting the error (mutation check).
- `bun test` on RightSidebar, hooks/useStableReference,
utils/{messages,agentSkills,ui,rightSidebarLayout},
stores/GitStatusStore: 1141 pass.
- `make static-check` green.

LOC: production +11 / −585; tests +134 / −355.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high` • Cost: `$4.21`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high
costs=4.21 -->
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