- Document target terminal module layout and dependency rules - Outline test split, facade contract, and follow-up bd issues
11 KiB
Terminal Extraction Plan
Summary
Refactor apps/web/app/terminal.tsx from a single 7,974-line client module into a feature folder at apps/web/terminal/*, while keeping apps/web/app/terminal.tsx as a temporary compatibility facade in the first pass.
This first extraction is a medium-scope, behavior-preserving refactor:
- no product behavior changes
- no route behavior changes
- no visual redesign
- no data model changes
- no immediate deletion of the old import surface
Current baseline is healthy and must remain healthy after the refactor:
bun test apps/web/app/terminal.test.ts apps/web/app/routes.test.tspassesbun --cwd=apps/web run buildpasses
Target Structure
Create this feature layout:
apps/web/terminal/
index.ts
state.tsx
shell.tsx
routes.tsx
core/
format.ts
filters.ts
route-config.ts
tape-data.ts
signals.ts
live-manifest.ts
hooks/
use-tape-data.ts
use-live-session.ts
use-virtual-tape.ts
components/
chrome.tsx
chart.tsx
drawers.tsx
panes.tsx
tests/
core.test.ts
live-manifest.test.ts
signals.test.ts
tape-data.test.ts
Keep this file in place for the first pass:
apps/web/app/terminal.tsx
Its final first-pass role is:
"use client"entrypoint- thin re-export facade only
- no business logic
- no React state
- no websocket/session logic
- no chart implementation
- target size: under 120 lines
Dependency Rules
Use this dependency direction and do not violate it:
core/*may depend only on shared types and othercore/*hooks/*may depend oncore/*components/*may depend oncore/*andhooks/*state.tsxmay depend oncore/*,hooks/*, andcomponents/*types only as neededshell.tsxandroutes.tsxmay depend onstate.tsxandcomponents/*index.tsre-exports public feature symbolsapp/terminal.tsxre-exports fromapps/web/terminal/index.ts
Do not allow circular imports.
Module Mapping
Move code out of terminal.tsx in this order.
1. Pure helpers first
Move non-React helpers into apps/web/terminal/core/*:
-
core/route-config.tsgetRouteFeaturesgetTapeVirtualConfigshouldIncludeEquitiesForDarkUnderlyingFallback
-
core/live-manifest.tsgetLiveManifestgetLiveHistoryRetentionCapgetScopedLiveAutoHydrationChannelsgetLiveFeedStatusgetHotChannelFeedStatus
-
core/tape-data.tsmergeNewestWithOverflowcomposeTapeItemsreducePausableTapeDataflushPausableTapeDataappendHistoryTailprojectPausableTapeStatefindAnchorRestoreIndexshouldRetainLiveSnapshotHistoryshouldShowEquitiesSilentFeedWarning- tape/history support types used only by these helpers
-
core/format.tsformatCompactUsdformatOptionContractLabelgetOptionTableSnapshot- price/size/time/date/contract formatting helpers that support UI rendering
-
core/signals.tsnormalizeAlertSeverityderiveAlertDirectiongetAlertWindowAnchorTsselectPrimaryClassifierHitclassifierToneForFamilysmartMoneyToneForProfilesmartMoneyProfileLabel
-
core/filters.tsbuildDefaultFlowFilterscountActiveFlowFilterGroupstoggleFilterValuenextFlowFilterPopoverState
These files must not include "use client".
2. Extract hooks and session logic
Move React hooks into apps/web/terminal/hooks/*:
-
hooks/use-virtual-tape.tsuseListScrolluseScrollAnchoruseVirtualHistoryGateuseTapeVirtualList
-
hooks/use-tape-data.tsuseTapeusePausableTapeViewuseLiveStreamuseFlowStreamstatusLabel- internal tape state types
-
hooks/use-live-session.tsuseLiveSession- live history endpoint constants
- live history query builders
- subscription dedupe helpers
- session-local types
Keep signatures stable unless a change is required to break a circular dependency. If a signature changes, update all callers in the same PR.
3. Extract UI components
Move rendering code into apps/web/terminal/components/*:
-
components/chrome.tsxTapeStatusTapeControlsPageFramePaneShellMetricStripFlowFilterPopover- local filter UI helpers
-
components/chart.tsxCandleChart- chart-only local types and overlay helpers
- isolate
lightweight-chartsusage here
-
components/drawers.tsxAlertSeverityStripAlertDrawerClassifierHitDrawerSmartMoneyDrawerDarkDrawer
-
components/panes.tsxOptionsPaneEquitiesPaneFlowPaneAlertsPaneClassifierPaneDarkPaneChartPaneFocusPaneReplayConsole
4. Extract state orchestration
Create apps/web/terminal/state.tsx for:
useTerminalStateTerminalContextuseTerminal
This file owns:
- route-aware feature selection
- filter input state
- selected entity/drawer state
- scroll-anchor wiring
- assembly of hook outputs into the single terminal state object
Keep useTerminalState internal. Do not export it from the feature barrel.
5. Extract shell and routes
Create:
-
apps/web/terminal/shell.tsxTerminalAppShell
-
apps/web/terminal/routes.tsxNAV_ITEMSOverviewRouteTapeRouteSignalsRouteChartsRouteReplayRoute
Important first-pass rule:
- keep existing route behavior exactly as-is
- app/page.tsx, app/layout.tsx, and app/tape/page.tsx may continue importing from
./terminal/../terminal - app/signals/page.tsx, app/charts/page.tsx, and app/replay/page.tsx must remain redirect pages in this pass
Facade Contract
Replace apps/web/app/terminal.tsx with a facade that re-exports from apps/web/terminal/index.ts.
The facade must continue exporting these symbols in the first pass:
getTapeVirtualConfigshouldIncludeEquitiesForDarkUnderlyingFallbackgetRouteFeaturesmergeNewestWithOverflowcomposeTapeItemsreducePausableTapeDataflushPausableTapeDataappendHistoryTailgetLiveHistoryRetentionCapgetScopedLiveAutoHydrationChannelsgetLiveFeedStatusgetHotChannelFeedStatusfindAnchorRestoreIndexformatCompactUsdformatOptionContractLabelnormalizeAlertSeverityderiveAlertDirectiongetAlertWindowAnchorTsbuildDefaultFlowFilterscountActiveFlowFilterGroupstoggleFilterValuenextFlowFilterPopoverStateprojectPausableTapeStateshouldShowEquitiesSilentFeedWarningshouldRetainLiveSnapshotHistoryselectPrimaryClassifierHitclassifierToneForFamilysmartMoneyToneForProfilesmartMoneyProfileLabelgetOptionTableSnapshotstatusLabelgetLiveManifestNAV_ITEMSFlowFilterPopoverTerminalAppShellOverviewRouteTapeRouteSignalsRouteChartsRouteReplayRoute
Do not add new facade-only exports.
Test Plan
Restructure tests so pure logic is tested from its final home instead of through the facade.
Keep
apps/web/app/routes.test.ts- still verifies redirect behavior for
/signals,/charts,/replay
- still verifies redirect behavior for
Split app/terminal.test.ts into feature tests
-
apps/web/terminal/tests/live-manifest.test.ts- route feature mapping
- manifest composition
- nav items if still treated as route metadata
-
apps/web/terminal/tests/tape-data.test.ts- merge/dedupe logic
- pausable tape behavior
- history seam behavior
- anchor restore behavior
- retention cap behavior
- scoped history behavior
-
apps/web/terminal/tests/core.test.ts- option contract formatting
- compact USD formatting
- option table snapshot formatting
- flow filter helpers
-
apps/web/terminal/tests/signals.test.ts- alert severity normalization
- direction derivation
- alert window anchor
- classifier/smart-money label and tone helpers
- live status labeling if kept outside tape-data tests
Optional and recommended:
- add one tiny
apps/web/app/terminal-facade.test.tsthat imports the facade and asserts a few critical exports exist, so we notice accidental facade breakage during the transition
Validation Gates
Implementation is not complete unless all of these pass:
bun test apps/web/terminal/tests apps/web/app/routes.test.tsbun --cwd=apps/web run build- Existing behavior smoke check:
/still renders the shell and overview/tapestill renders shell and tape panes/signals,/charts,/replaystill redirect to/
apps/web/app/terminal.tsxis a facade only and contains no moved logic- No extracted pure helper file contains React imports
- No new circular imports are introduced
Non-Goals For This Pass
Do not do these in the first extraction:
- redesign panes or drawers
- change websocket or replay behavior
- change route inventory
- remove unused legacy route exports
- change CSS structure beyond import fixes
- optimize bundle size as a separate objective
- rewrite tests to different testing tools
Beads Follow-Up Issues To File
Create these bd issues during implementation if they do not already exist:
-
task, priority2Title:Remove temporary apps/web/app/terminal.tsx facade after terminal imports are migratedDescription: track deletion of the compatibility facade once route/layout/test imports point at finalapps/web/terminal/*modules -
task, priority3Title:Audit and remove dead terminal route exports no longer used by app redirectsDescription: verify whetherSignalsRoute,ChartsRoute, andReplayRouteshould be deleted since App Router pages now redirect to/
If additional cleanup is discovered during extraction, create linked bd tasks with discovered-from dependencies rather than expanding this refactor mid-flight.
Acceptance Criteria
The first extraction is successful when:
- terminal logic is split into the target
apps/web/terminal/*structure apps/web/app/terminal.tsxremains only as a thin compatibility layer- app entrypoints continue to work without behavior changes
- tests target the new module homes for pure logic
- build and tests pass
- follow-up
bdissues exist for facade removal and dead-export cleanup
Assumptions And Defaults
- Chosen scope: medium slice, not full architectural rewrite
- Chosen transition: keep
apps/web/app/terminal.tsxas a temporary facade - Chosen module home:
apps/web/terminal/*, notapps/web/app/terminal/* - Default behavior requirement: strict behavioral parity
- Default testing approach: split existing monolithic helper tests by concern and colocate them under
apps/web/terminal/tests - Default routing approach: keep redirect pages untouched in the first pass