Skip to content

Split brains-pane.tsx into sidebar, detail pane, and collection view - #925

Merged
selfcontained merged 1 commit into
mainfrom
componentizer/split-brains-pane
Aug 10, 2026
Merged

Split brains-pane.tsx into sidebar, detail pane, and collection view#925
selfcontained merged 1 commit into
mainfrom
componentizer/split-brains-pane

Conversation

@selfcontained

Copy link
Copy Markdown
Owner

What

apps/web/src/components/app/brains-pane.tsx was a 704-line file holding seven components. This splits it along the boundaries that were already there — every component moved wholesale.

FileLinesContents
brains-pane.tsx123BrainsListContent — the sidebar project list
brains-detail-pane.tsx276BrainsDetailPane, BrainsOverview, BrainProjectDetail, CollectionPill
brains-collection-view.tsx300BrainCollectionView, DeleteBrainDialog, shared DeleteTarget type
brains-utils.ts3repoBasename — used by both the sidebar and the detail header

automations-pane.tsx is the sole consumer; its import block now pulls BrainsDetailPane from the new file. No barrel re-export was added, matching the convention from #855, #883, and #913.

Why it was a candidate

704 lines, three logically independent UI regions (sidebar / project detail chrome / collection contents), and a compound-component shape that had grown over time. It was the queued next_focus from the previous run (recorded there as 482 lines — that count was actually brain-cards.tsx).

Behavior

Purely structural. No hook changed owner, so neither the state-lifetime hazard from #803 nor the effect-registration-order hazard from #870 applies here. The one non-mechanical change is that the 4-variant delete-target union — previously written out twice, once in BrainCollectionView's useState and once in DeleteBrainDialog's props — is now a single DeleteTarget type in the file that owns both.

Validation

  • pnpm run finalize:web — type check + production build green
  • pnpm run test:e2e — 178 passed, 12 skipped (terminal-live, pre-existing)
  • Playwright visual pass on a seeded dev stack: sidebar empty + populated states, project detail header, collection pills (routing + active state), search filter, all three card sections, and both dialogs (DeleteBrainDialog and clear-project) open and cancel correctly. 0 console errors.

Next run

path-input.tsx (531 lines, 14 hooks) — flagged higher-risk for focus/keyboard timing.

brains-pane.tsx held seven components in one 704-line file. Split along the
existing component boundaries — every component moved wholesale, so no state
or effect changed owner and the rendered output is identical.
- brains-pane.tsx (123): BrainsListContent, the sidebar project list
- brains-detail-pane.tsx (276): BrainsDetailPane, BrainsOverview,
BrainProjectDetail, CollectionPill
- brains-collection-view.tsx (300): BrainCollectionView, DeleteBrainDialog,
plus a shared DeleteTarget type that was previously spelled out twice
- brains-utils.ts (3): repoBasename, used by the sidebar and the detail header
automations-pane.tsx is the sole consumer; its import block now pulls
BrainsDetailPane from the new file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@selfcontained
selfcontained merged commit b8649f1 into mainAug 10, 2026
1 check passed
@selfcontained
selfcontained deleted the componentizer/split-brains-pane branch August 10, 2026 08:15
selfcontained added a commit that referenced this pull request Aug 14, 2026
* Cover the Brain Explorer panes and fix two routing bugs
Closes the last two zero-coverage surfaces from the #925 brains split, and
fixes two real bugs the coverage work surfaced in brains-detail-pane.tsx.
A malformed base64url repo root recovered by calling navigate() during
render. React Router v7 refuses imperative navigation during render — it
warns and no-ops — so the undecodable root stayed in the address bar and
came straight back on reload or from a shared link. It is now a declarative
<Navigate replace />.
The collection path segment was decoded a second time on the way to the
collection view, but react-router has already decoded it. A collection named
"50%off" threw URIError and took the pane down; one named "%41" silently
selected a collection that does not exist. Both names are reachable — the
sidebar links to whatever collection an agent created.
brains-detail-pane.test.tsx (20 tests) mounts the pane on the same three
routes the real router declares and pins the repo-root decode and its
malformed fallback, the collection round trip, the pill counts and active
marker, the search wiring, and the destructive "Clear project" flow
including its wire contract, summed toast, failure path and pending gating.
16/17 mutants killed; the survivor is the collections loading guard, which
is unobservable because the query's data already defaults to [].
brains-pane.test.tsx (14 tests) pins the encode half of the same URL
contract plus the sidebar's list states, per-type count gating, keyboard
activation and selection highlight. 18/18 mutants killed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Address review: assert the replace semantics, drop two hollow tests
The first battery never mutated the { replace: true } flags, so nothing
guarded the half of the malformed-root fix that matters most: pushing
instead of replacing leaves the undecodable URL one Back press away, and
going Back redirects forward again — a trap. A navigation-type probe now
pins REPLACE on all three redirects and PUSH on ordinary pill navigation.
Deleted the loading-pills test. It could not fail: useBrainCollections
already defaults data to [], so both collectionsLoading guards are
unobservable, and the assertion filtered for a collection no fixture
declared. The dead guards are recorded for a future run rather than
covered by a test that certifies nothing.
Renamed the post-delete test to the navigation it actually protects —
leaving the project unmounts the dialog either way, so that test cannot be
what proves the dialog closes.
Also: dropped a sidebar test that duplicated the routing case, split the
Enter/Space test so neither reuses a node captured before its own route
change, tightened the pill count assertions to exact text, relaxed the
skeleton count to presence, and stubbed Toaster alongside toast.
Batteries after the changes: 20/21 on brains-detail-pane.tsx (the survivor
is the proven-unobservable loading guard) and 19/19 on brains-pane.tsx.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto 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

@selfcontained