From 96e2840b9bfa66d0bcd98e5341a3337a0e98578b Mon Sep 17 00:00:00 2001 From: Sai Kiran Vella Date: Thu, 4 Jun 2026 16:28:21 -0400 Subject: [PATCH] phase-5: code maintenance --- CLAUDE.md | 81 +++-- ui/src/lib/AnalysisSidebar.svelte | 198 +++++++++++ ui/src/lib/AssetTable.svelte | 108 ++++++ ui/src/lib/MarketContextStrip.svelte | 69 ++++ ui/src/lib/VerdictPill.svelte | 6 + ui/src/routes/+layout.svelte | 23 +- ui/src/routes/+page.js | 13 + ui/src/routes/+page.svelte | 471 +++------------------------ ui/src/routes/safe-buys/+page.svelte | 19 +- ui/src/styles/_layout.scss | 16 - 10 files changed, 525 insertions(+), 479 deletions(-) create mode 100644 ui/src/lib/AnalysisSidebar.svelte create mode 100644 ui/src/lib/AssetTable.svelte create mode 100644 ui/src/lib/MarketContextStrip.svelte create mode 100644 ui/src/lib/VerdictPill.svelte create mode 100644 ui/src/routes/+page.js diff --git a/CLAUDE.md b/CLAUDE.md index 06f22ed..fd2d8df 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 4–5 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 ` diff --git a/ui/src/lib/AssetTable.svelte b/ui/src/lib/AssetTable.svelte new file mode 100644 index 0000000..a36f361 --- /dev/null +++ b/ui/src/lib/AssetTable.svelte @@ -0,0 +1,108 @@ + + +
+
+

{type}S

+ {rows.length} + +
+ + +
+ + +
+ +
+ + + + + + + + {#if type === 'STOCK'} + + + + + {:else if type === 'ETF'} + + {:else} + + {/if} + + + + {#each sorted(rows) as r} + {@const m = r.asset.displayMetrics ?? {}} + {@const v = r[mode]} + + + + + + {#if type === 'STOCK'} + + + + + + + + + {:else if type === 'ETF'} + + + + + {:else} + + + + {/if} + + {/each} + +
TickerPriceVerdictScoreSectorP/EPEGROE%OpMgn%FCF%D/EFlagsExpenseYieldAUM5Y RetYTMDurationRating
{r.asset.ticker}{m.Price ?? '—'}{v.scoreSummary}{m.Sector ?? '—'}{m['P/E'] ?? '—'}{m['PEG'] ?? '—'}{m['ROE%'] ?? '—'}{m['OpMgn%'] ?? '—'}{m['FCF Yld%'] ?? '—'}{m['D/E'] ?? '—'} + {#each v.audit?.riskFlags ?? [] as flag} + ⚠ {flag} + {/each} + {m['Exp Ratio%'] ?? '—'}{m['Yield%'] ?? '—'}{m['AUM'] ?? '—'}{m['5Y Return%'] ?? '—'}{m['YTM%'] ?? '—'}{m['Duration'] ?? '—'}{m['Rating'] ?? '—'}
+
+
+ + diff --git a/ui/src/lib/MarketContextStrip.svelte b/ui/src/lib/MarketContextStrip.svelte new file mode 100644 index 0000000..acbb09f --- /dev/null +++ b/ui/src/lib/MarketContextStrip.svelte @@ -0,0 +1,69 @@ + + +
+ {#each chips as chip} +
+ {chip.label} + + {chip.value ?? '—'} + +
+ {/each} +
+ + diff --git a/ui/src/lib/VerdictPill.svelte b/ui/src/lib/VerdictPill.svelte new file mode 100644 index 0000000..f6c3aab --- /dev/null +++ b/ui/src/lib/VerdictPill.svelte @@ -0,0 +1,6 @@ + + +{verdictShort(label)} diff --git a/ui/src/routes/+layout.svelte b/ui/src/routes/+layout.svelte index 5ad9a49..3bb573e 100644 --- a/ui/src/routes/+layout.svelte +++ b/ui/src/routes/+layout.svelte @@ -1,13 +1,17 @@ @@ -16,10 +20,10 @@ @@ -34,8 +38,7 @@ {#if $navigating} {:else} {@render children()} diff --git a/ui/src/routes/+page.js b/ui/src/routes/+page.js new file mode 100644 index 0000000..b3be7cf --- /dev/null +++ b/ui/src/routes/+page.js @@ -0,0 +1,13 @@ +import { fetchCatalysts, screenTickers } from '$lib/api.js'; + +// Client-only — the API lives at localhost:3000, not accessible during SSR +export const ssr = false; + +export async function load() { + const cat = await fetchCatalysts(); + const results = await screenTickers(cat.tickers); + return { + results, + catalystInput: cat.tickers.join(', '), + }; +} diff --git a/ui/src/routes/+page.svelte b/ui/src/routes/+page.svelte index 9938331..f0854a1 100644 --- a/ui/src/routes/+page.svelte +++ b/ui/src/routes/+page.svelte @@ -1,25 +1,25 @@