phase-5: code maintenance

This commit is contained in:
Kazuma
2026-06-04 16:28:21 -04:00
parent 104ed81b9f
commit 16bd95aa85
10 changed files with 525 additions and 479 deletions
+56 -25
View File
@@ -115,11 +115,33 @@ server/
calls.js ← CRUD for market calls + GET /api/calls/calendar (earnings/dividend events)
ui/ ← SvelteKit dashboard (lives inside this repo, not a separate repo)
src/routes/
+page.svelte ← main screener UI
calls/ ← market calls list + detail views
portfolio/ ← portfolio advice view
safe-buys/ ← filtered strong-buy view
src/
styles/ ← global SCSS design-token system (Phase 4)
app.scss ← root file — @use all partials
_tokens.scss ← CSS custom properties generated from SCSS maps ($bg, $text, $blues, $signals…)
_reset.scss ← box-sizing reset + body base
_layout.scss ← shell, nav, .links, main, nav-progress, .loading-area
_section.scss ← .section, .section-header, .count, .mode-tabs, .error-banner
_table.scss ← table, thead/tbody, .table-wrap, .col-ticker, .ticker, .num, .tag
_buttons.scss ← button base, .btn-primary, .btn-catalyst, .btn-ghost, .btn-screen, .btn-analyze
_badges.scss ← .verdict-pill, .sentiment-pill, .text-* helpers (SCSS @each maps)
lib/
api.js ← typed fetch wrappers for all API routes
utils.ts ← shared pure functions: sigOrd, sorted, verdictShort, vClass, fmtPE, fmt…
MarketContext.svelte ← collapsible card-grid context (used in portfolio + safe-buys)
MarketContextStrip.svelte ← horizontal chip strip (used in screener — Phase 5)
AssetTable.svelte ← STOCK/ETF/BOND section: mode tabs + Analyze + table (Phase 5)
AnalysisSidebar.svelte ← LLM analysis slide-over panel (Phase 5)
VerdictPill.svelte ← verdict-pill span; props: label (Phase 5)
SignalBadge.svelte ← signal emoji + label badge
Spinner.svelte ← sm: dot-pulse | md/lg: chart-line animation
routes/
+page.js ← SvelteKit load (ssr:false) — fetches catalysts + screens on mount (Phase 5)
+page.svelte ← main screener UI (~230 lines after Phase 5 decomposition)
+layout.svelte ← shell, nav, nav-progress bar, nav-overlay with Spinner
calls/ ← market calls list + detail views
portfolio/ ← portfolio advice view
safe-buys/ ← filtered strong-buy view
market-calls.json ← persisted market thesis calls (written by MarketCallStore)
portfolio.json ← user's holdings: ticker, shares, costBasis, source, type
@@ -358,6 +380,12 @@ Test output: silent on pass, shows only failures + one summary line (`scripts/su
**Key unit:** `ytm` in `Bond.metrics` is stored as a percentage (e.g. `6.5` = 6.5%). `BondScorer._sanitize` divides by 100 before spread calculation.
**Coverage gaps (known):**
- `MarketCallStore.js` — no tests; CRUD against `market-calls.json` is untested
- `LLMAnalyst.test.js` — tests a local copy of the fence-stripping regex rather than importing from source; will silently drift if the regex changes
- API routes (`server/server/routes/`) — no integration tests; covered implicitly by manual testing only
- UI components — not tested at the unit level (Phases 45 are pure UI; no server logic changed)
---
## Conventions
@@ -412,28 +440,31 @@ All items completed. Additional features delivered alongside cleanup:
- Renamed `src/` to `server/``src/server/` is now `server/server/`
- Updated all import paths in `bin/`, `tests/`, and `CLAUDE.md`
### Phase 4 — SCSS Migration
Replace per-component `<style>` blocks with a shared token system in `ui/src/styles/`:
```
_tokens.scss ← all color variables, spacing, font-size scale
_reset.scss ← current 3-line app.css
_layout.scss ← shell, nav, main (from +layout.svelte)
_table.scss ← shared table/thead/.ticker/.num styles (used across 4 pages)
_buttons.scss ← btn-primary, btn-ghost, btn-analyze, btn-catalyst
_badges.scss ← verdict-pill, tag, sentiment-pill (resolves verdict-pill vs vpill inconsistency)
_section.scss ← section card + section-header pattern
app.scss ← root file, @uses all partials
```
Component `<style>` blocks should only contain styles genuinely unique to that component.
### Phase 4 — SCSS Migration ✅ COMPLETE
### Phase 5 — Decompose `+page.svelte`
At 962 lines it is the biggest maintenance liability. Extract into:
- `AssetTable.svelte` — accepts `type`, `rows`, `mode`; renders STOCK/ETF/BOND table with mode tabs and Analyze button
- `AnalysisSidebar.svelte` — owns open/close/loading state, takes `type` + `onAnalyze` as props
- `VerdictPill.svelte` — single component replacing both `.verdict-pill` and `.vpill` usages
- `MarketContextStrip.svelte` — consolidates the inline version in `+page.svelte` with the existing `MarketContext.svelte` in `$lib/`
**Done:**
- Created `ui/src/styles/` with 7 partials + `app.scss` root (imported by `+layout.svelte`)
- `_tokens.scss` uses SCSS maps (`$bg`, `$borders`, `$text`, `$blues`, `$signals`) with `@each` loops to emit CSS custom properties — adding a token is one line in the map
- `_badges.scss` uses `@each` + `map.get` for verdict/sentiment color variants and `.text-*` helpers; shared `%pill-base` placeholder
- `_buttons.scss` uses `%btn-disabled` / `%btn-inline-flex` placeholders + nested `&:hover` / `&:disabled`
- `_section.scss`, `_table.scss`, `_layout.scss` use SCSS nesting throughout
- `.vpill` (safe-buys) unified with `.verdict-pill` (screener) — inconsistency resolved
- All component `<style>` blocks trimmed to component-unique rules only; `+layout.svelte` style block removed entirely
- Nav links now highlight immediately on click via `activePath` derived from `$navigating` (not `$page`)
- `+layout.svelte` nav-overlay uses `<Spinner>` component instead of legacy CSS spinner
Also: split `loadCatalysts()` into two functions (fetch tickers / screen tickers). Replace the `_booted` / `$effect` hack with a proper `+page.js` load function.
**Note:** `sass` must be installed in `ui/` (`npm install -D sass --legacy-peer-deps`). Map keys that are CSS color names (`'green'`, `'red'`, `'blue'`, etc.) must be quoted to avoid Sass color-value interpolation warnings.
### Phase 5 — Decompose `+page.svelte` ✅ COMPLETE
**Done:**
- `VerdictPill.svelte` — wraps `<span class="verdict-pill {vClass(label)}">`. Used in screener summary, detail tables, and safe-buys (replacing inline spans + removing `verdictShort`/`vClass` imports from safe-buys)
- `MarketContextStrip.svelte` — horizontal chip strip extracted from `+page.svelte`. Uses a `$derived` chips array so the template is declarative (no repeated markup blocks)
- `AssetTable.svelte` — full STOCK/ETF/BOND section: section-header, mode tabs (owns `mode` state internally), Analyze button, complete table per type. Props: `type`, `rows`, `analyzeLoading`, `onAnalyze`
- `AnalysisSidebar.svelte` — LLM slide-over panel. Props: `sidebar` (state object from parent), `onClose`. All `sb-*` styles live here
- `+page.js``export const ssr = false`; `load()` fetches catalysts then screens them. Component receives `data.results` + `data.catalystInput` as props — replaces `_booted` / `$effect` hack entirely
- `loadCatalysts()` split: initial load handled by `+page.js`, user-triggered refresh is `reloadCatalysts()` in the component
- `+page.svelte` reduced from ~600 lines to ~230 lines
### Phase 6 — TypeScript
Convert server first (no framework coupling), then `$lib/utils`, then Svelte components.