- Document target terminal module layout and dependency rules - Outline test split, facade contract, and follow-up bd issues
372 lines
11 KiB
Markdown
372 lines
11 KiB
Markdown
# Terminal Extraction Plan
|
|
|
|
## Summary
|
|
|
|
Refactor [`apps/web/app/terminal.tsx`](/Users/kell/Cloud/dev/islandflow/apps/web/app/terminal.tsx:1) 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.ts` passes
|
|
- `bun --cwd=apps/web run build` passes
|
|
|
|
## Target Structure
|
|
|
|
Create this feature layout:
|
|
|
|
```text
|
|
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 other `core/*`
|
|
- `hooks/*` may depend on `core/*`
|
|
- `components/*` may depend on `core/*` and `hooks/*`
|
|
- `state.tsx` may depend on `core/*`, `hooks/*`, and `components/*` types only as needed
|
|
- `shell.tsx` and `routes.tsx` may depend on `state.tsx` and `components/*`
|
|
- `index.ts` re-exports public feature symbols
|
|
- `app/terminal.tsx` re-exports from `apps/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.ts`
|
|
- `getRouteFeatures`
|
|
- `getTapeVirtualConfig`
|
|
- `shouldIncludeEquitiesForDarkUnderlyingFallback`
|
|
|
|
- `core/live-manifest.ts`
|
|
- `getLiveManifest`
|
|
- `getLiveHistoryRetentionCap`
|
|
- `getScopedLiveAutoHydrationChannels`
|
|
- `getLiveFeedStatus`
|
|
- `getHotChannelFeedStatus`
|
|
|
|
- `core/tape-data.ts`
|
|
- `mergeNewestWithOverflow`
|
|
- `composeTapeItems`
|
|
- `reducePausableTapeData`
|
|
- `flushPausableTapeData`
|
|
- `appendHistoryTail`
|
|
- `projectPausableTapeState`
|
|
- `findAnchorRestoreIndex`
|
|
- `shouldRetainLiveSnapshotHistory`
|
|
- `shouldShowEquitiesSilentFeedWarning`
|
|
- tape/history support types used only by these helpers
|
|
|
|
- `core/format.ts`
|
|
- `formatCompactUsd`
|
|
- `formatOptionContractLabel`
|
|
- `getOptionTableSnapshot`
|
|
- price/size/time/date/contract formatting helpers that support UI rendering
|
|
|
|
- `core/signals.ts`
|
|
- `normalizeAlertSeverity`
|
|
- `deriveAlertDirection`
|
|
- `getAlertWindowAnchorTs`
|
|
- `selectPrimaryClassifierHit`
|
|
- `classifierToneForFamily`
|
|
- `smartMoneyToneForProfile`
|
|
- `smartMoneyProfileLabel`
|
|
|
|
- `core/filters.ts`
|
|
- `buildDefaultFlowFilters`
|
|
- `countActiveFlowFilterGroups`
|
|
- `toggleFilterValue`
|
|
- `nextFlowFilterPopoverState`
|
|
|
|
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.ts`
|
|
- `useListScroll`
|
|
- `useScrollAnchor`
|
|
- `useVirtualHistoryGate`
|
|
- `useTapeVirtualList`
|
|
|
|
- `hooks/use-tape-data.ts`
|
|
- `useTape`
|
|
- `usePausableTapeView`
|
|
- `useLiveStream`
|
|
- `useFlowStream`
|
|
- `statusLabel`
|
|
- internal tape state types
|
|
|
|
- `hooks/use-live-session.ts`
|
|
- `useLiveSession`
|
|
- 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.tsx`
|
|
- `TapeStatus`
|
|
- `TapeControls`
|
|
- `PageFrame`
|
|
- `Pane`
|
|
- `ShellMetricStrip`
|
|
- `FlowFilterPopover`
|
|
- local filter UI helpers
|
|
|
|
- `components/chart.tsx`
|
|
- `CandleChart`
|
|
- chart-only local types and overlay helpers
|
|
- isolate `lightweight-charts` usage here
|
|
|
|
- `components/drawers.tsx`
|
|
- `AlertSeverityStrip`
|
|
- `AlertDrawer`
|
|
- `ClassifierHitDrawer`
|
|
- `SmartMoneyDrawer`
|
|
- `DarkDrawer`
|
|
|
|
- `components/panes.tsx`
|
|
- `OptionsPane`
|
|
- `EquitiesPane`
|
|
- `FlowPane`
|
|
- `AlertsPane`
|
|
- `ClassifierPane`
|
|
- `DarkPane`
|
|
- `ChartPane`
|
|
- `FocusPane`
|
|
- `ReplayConsole`
|
|
|
|
### 4. Extract state orchestration
|
|
|
|
Create `apps/web/terminal/state.tsx` for:
|
|
- `useTerminalState`
|
|
- `TerminalContext`
|
|
- `useTerminal`
|
|
|
|
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.tsx`
|
|
- `TerminalAppShell`
|
|
|
|
- `apps/web/terminal/routes.tsx`
|
|
- `NAV_ITEMS`
|
|
- `OverviewRoute`
|
|
- `TapeRoute`
|
|
- `SignalsRoute`
|
|
- `ChartsRoute`
|
|
- `ReplayRoute`
|
|
|
|
Important first-pass rule:
|
|
- keep existing route behavior exactly as-is
|
|
- [app/page.tsx](/Users/kell/Cloud/dev/islandflow/apps/web/app/page.tsx:1), [app/layout.tsx](/Users/kell/Cloud/dev/islandflow/apps/web/app/layout.tsx:4), and [app/tape/page.tsx](/Users/kell/Cloud/dev/islandflow/apps/web/app/tape/page.tsx:1) may continue importing from `./terminal` / `../terminal`
|
|
- [app/signals/page.tsx](/Users/kell/Cloud/dev/islandflow/apps/web/app/signals/page.tsx:1), [app/charts/page.tsx](/Users/kell/Cloud/dev/islandflow/apps/web/app/charts/page.tsx:1), and [app/replay/page.tsx](/Users/kell/Cloud/dev/islandflow/apps/web/app/replay/page.tsx:1) 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:
|
|
|
|
- `getTapeVirtualConfig`
|
|
- `shouldIncludeEquitiesForDarkUnderlyingFallback`
|
|
- `getRouteFeatures`
|
|
- `mergeNewestWithOverflow`
|
|
- `composeTapeItems`
|
|
- `reducePausableTapeData`
|
|
- `flushPausableTapeData`
|
|
- `appendHistoryTail`
|
|
- `getLiveHistoryRetentionCap`
|
|
- `getScopedLiveAutoHydrationChannels`
|
|
- `getLiveFeedStatus`
|
|
- `getHotChannelFeedStatus`
|
|
- `findAnchorRestoreIndex`
|
|
- `formatCompactUsd`
|
|
- `formatOptionContractLabel`
|
|
- `normalizeAlertSeverity`
|
|
- `deriveAlertDirection`
|
|
- `getAlertWindowAnchorTs`
|
|
- `buildDefaultFlowFilters`
|
|
- `countActiveFlowFilterGroups`
|
|
- `toggleFilterValue`
|
|
- `nextFlowFilterPopoverState`
|
|
- `projectPausableTapeState`
|
|
- `shouldShowEquitiesSilentFeedWarning`
|
|
- `shouldRetainLiveSnapshotHistory`
|
|
- `selectPrimaryClassifierHit`
|
|
- `classifierToneForFamily`
|
|
- `smartMoneyToneForProfile`
|
|
- `smartMoneyProfileLabel`
|
|
- `getOptionTableSnapshot`
|
|
- `statusLabel`
|
|
- `getLiveManifest`
|
|
- `NAV_ITEMS`
|
|
- `FlowFilterPopover`
|
|
- `TerminalAppShell`
|
|
- `OverviewRoute`
|
|
- `TapeRoute`
|
|
- `SignalsRoute`
|
|
- `ChartsRoute`
|
|
- `ReplayRoute`
|
|
|
|
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`
|
|
|
|
### 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.ts` that 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:
|
|
|
|
1. `bun test apps/web/terminal/tests apps/web/app/routes.test.ts`
|
|
2. `bun --cwd=apps/web run build`
|
|
3. Existing behavior smoke check:
|
|
- `/` still renders the shell and overview
|
|
- `/tape` still renders shell and tape panes
|
|
- `/signals`, `/charts`, `/replay` still redirect to `/`
|
|
4. `apps/web/app/terminal.tsx` is a facade only and contains no moved logic
|
|
5. No extracted pure helper file contains React imports
|
|
6. 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:
|
|
|
|
1. `task`, priority `2`
|
|
Title: `Remove temporary apps/web/app/terminal.tsx facade after terminal imports are migrated`
|
|
Description: track deletion of the compatibility facade once route/layout/test imports point at final `apps/web/terminal/*` modules
|
|
|
|
2. `task`, priority `3`
|
|
Title: `Audit and remove dead terminal route exports no longer used by app redirects`
|
|
Description: verify whether `SignalsRoute`, `ChartsRoute`, and `ReplayRoute` should 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.tsx` remains 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 `bd` issues 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.tsx` as a temporary facade
|
|
- Chosen module home: `apps/web/terminal/*`, not `apps/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
|