11 — Changelog
Changelog
Note: The product was renamed from ValueLens to GrahamScreener on 2026-05-09. Historical entries below retain the original name for accuracy.
All notable changes to GrahamScreener are documented in this file.
Format follows Keep a Changelog.
[1.5.9] - 2026-05-11
Changed
- Production alert emails now send from
alerts@grahamscreener.com— Resend domain verification completed forgrahamscreener.com. UpdatedALERT_FROM_EMAILVercel env var fromonboarding@resend.dev(sandbox) toalerts@grahamscreener.com. Emails now deliver to any recipient, not just the Resend account owner. - Alert emails verified end-to-end in production — confirmed delivery to
puran.2006@gmail.comfromalerts@grahamscreener.com(Resend ID:8f97003a-59d1-42c6-9de2-c936698af9b2). Production cron workflow (Hourly Alert Check #22) ran successfully.
Removed
/api/debug/test-emailendpoint — temporary endpoint for direct Resend email testing. No longer needed after domain verification./api/debug/alertsendpoint — temporary endpoint for dumping all alerts + cache + environment status. No longer needed./api/debug/seed-cacheendpoint — temporary endpoint for seeding snapshot_cache with test data. No longer needed.
[1.5.8] - 2026-05-11
Fixed
- Alert emails now deliver successfully — diagnosed and resolved end-to-end email delivery failure. Root cause: Resend sandbox mode (
onboarding@resend.dev) restricts recipients to the account owner's email only. Verified email pipeline works (Resend ID:90656969-782d-4be9-a865-cee6b3b72766). Production fix requires verifyinggrahamscreener.comdomain on Resend and switchingALERT_FROM_EMAILtoalerts@grahamscreener.com. /api/cron/check-alertsnow supports?testPrice=Nquery param — bypasses cache and Yahoo entirely, useful for testing alert condition evaluation without live market data
Added
/api/debug/test-emailendpoint (TEMPORARY) — directly tests Resend email delivery for AAPL alert, bypassing cache/Yahoo/condition logic. Delete after domain verification.
Changed
/api/debug/seed-cacheendpoint updated — now also updates alert threshold and email, clears debounce, and reads back both rows to verify Turso persistence
[1.5.7] - 2026-05-11
Added
/api/debug/seed-cacheendpoint (TEMPORARY) — POST endpoint that upserts a test AAPL price ($200.50) intosnapshot_cachefor end-to-end alert email verification. Delete after confirming emails work.
[1.5.6] - 2026-05-10
Changed
- Alert checker now uses cache-first pricing —
/api/cron/check-alertsreads fromsnapshot_cachetable (populated by GitHub Actions snapshots) instead of calling Yahoo directly. Yahoo is only used as a fallback when cache is missing or stale (>25 hours). If both fail, the individual alert is skipped without crashing the batch. - Added
priceSourcefield to alert diagnostics — logs and JSON response now show whether price came fromcache,yahoo, ornone
Fixed
- Yahoo 429 errors on Vercel — Vercel's shared IP pool triggers Yahoo rate-limits. Cache-first approach eliminates Yahoo calls in the common case (daily snapshot keeps cache <25h old)
[1.5.5] - 2026-05-10
Fixed
- Alert emails never reaching Resend — added comprehensive diagnostics to
/api/cron/check-alertsso the JSON response body (captured by GitHub Actions) now includes: active alert count, per-alert price fetch + condition evaluation + email send result, environment variable status, and a full trace log - Silent failures invisible —
sendPriceAlertnow logs Resend request/response details to console
Added
/api/debug/alertsendpoint (TEMPORARY) — returns all alerts in DB + environment variable status for production debugging. Delete after confirming emails work.- Diagnostic
details[]array in check-alerts response showing per-alert: ticker, current price, condition result, debounce status, email send outcome
[1.5.4] - 2026-05-10
Added
- Reply-To header on alert emails — replies go to
hello@grahamscreener.cominstead of the no-reply alerts address - Footer in app layout with copyright, email link, and GitHub link
/aboutpage with product description, founder note, contact emails, and open-source links- Contact section in README with
hello@andpartnerships@addresses .env.examplewith all environment variables documenteddocs/16_EMAIL_SETUP.md— Cloudflare Email Routing + Resend setup guide with DKIM/SPF records and Gmail "Send mail as" instructions
Changed
ALERT_REPLY_TOenv var added to alert email code (default:hello@grahamscreener.com)- Updated env var tables in
docs/12_DEPLOY.mdanddocs/13_ALERTS.md
[1.5.3] - 2026-05-10
Changed
- Dependabot now ignores all major version bumps —
ignore: [{ dependency-name: "*", update-types: ["version-update:semver-major"] }]added to both npm and github-actions ecosystems. Patch and minor updates remain auto-grouped weekly. Major upgrades require manualnpm install <pkg>@latestafter reading the migration guide. - Separated npm patches and minors into distinct Dependabot groups
- Added
github-actionslabel to Actions dependency PRs - Added PR limits: 5 npm, 3 Actions
- Added ACST-aware schedule (
08:00 Australia/Adelaide)
[1.5.2] - 2026-05-10
Changed
- Migrated hourly alert checks from Vercel Cron to GitHub Actions — Vercel Hobby (free tier) limits crons to daily, blocking deploy. New
check-alerts.ymlworkflow calls the same/api/cron/check-alertsendpoint hourly with no frequency limits on public repos. - Removed
cronsarray fromvercel.json - Added Vercel Pro migration path to
docs/13_ALERTS.md - Added alert check workflow to
docs/14_AUTOMATION.mdusage table
[1.4.1] - 2026-05-10
Fixed
- Removed unused
eqimport fromdrizzle-orminsrc/app/api/alerts/route.ts(lint error:@typescript-eslint/no-unused-vars) - Removed unused
TickerSearchimport insrc/components/alerts/alert-modal.tsx(lint error:@typescript-eslint/no-unused-vars). The existingTickerSearchcomponent navigates on selection and lacks anonSelectcallback, making it incompatible with the modal's form-field pattern. Ticker autocomplete in the alert modal is deferred to a future release.
[1.5.0] - 2026-05-10
Added
- GitHub Actions auto-deploy — push to
maintriggers type-check → build → deploy to Vercel via CLI (deploy.yml) - Daily watchlist snapshot — cron at 8am AEST fetches fresh Yahoo data for watchlist tickers into production Turso (
snapshot-watchlist.yml) - Weekly full universe snapshot — cron Sunday 2am AEST refreshes all 200 tickers (
snapshot-full.yml) - CI checks on PRs — lint, type-check, and build run on every pull request and non-main branch push (
ci.yml) - Dependabot — weekly npm + GitHub Actions dependency updates, grouped patch/minor PRs, auto-assigned (
dependabot.yml) - LICENSE file (MIT, copyright puran2006-lgtm)
- README status badges: deploy, license, live site, GitHub stars
docs/14_AUTOMATION.md— full GitHub Actions documentation- GitHub Actions secrets setup guide in
docs/12_DEPLOY.md - Automatic GitHub Issue creation on snapshot workflow failure (label:
automation-failure) - Retry-once policy on snapshot workflows (5-min wait between attempts)
[1.4.0] - 2026-05-10
Added
- Email price alerts — set target buy, stop loss, or percentage-change alerts on any ticker; evaluated hourly by Vercel Cron and delivered via Resend
alertstable in SQLite schema with 4 condition types, 24h debounce, and reference price for percentage alerts/alertspage with active/paused/recently-triggered sections, create/edit modal, pause/resume/delete actions- CRUD API routes:
GET/POST /api/alerts,PATCH/DELETE /api/alerts/[id] - Cron endpoint
GET /api/cron/check-alerts— evaluates all active alerts against live Yahoo prices, sends email on trigger src/lib/email.ts— Resend email helper with dark-themed HTML templatescripts/test-email.ts— CLI script to verify Resend setup (npm run test-email)- Demo alert seeded by
npm run seed(AAPL target buy at $100) - Vercel Cron schedule in
vercel.json(0 */1 * * *— hourly) /alertslink in sidebar nav +g akeyboard shortcutresenddependency (^4.8.0)docs/13_ALERTS.md— complete alerts documentation
Changed
- Sidebar nav expanded to 5 items (added Alerts with Bell icon)
- Mobile bottom nav changed from 4-col to 5-col grid
vercel.jsonupdated with cron config and maxDuration for check-alerts route
[1.3.4] - 2026-05-10
Changed
- Snapshot CLI (
scripts/snapshot.ts) rewritten for Yahoo rate-limit resilience: sequential processing (no parallel workers), 1.5–2.5s random jitter between requests, exponential backoff on 429 (30s → 60s → 120s), 5-minute pause after 3 consecutive 429s - Added CLI flags:
--limit N(cap ticker count),--tickers AAPL,CBA.AX(specific tickers),--watchlist-only(snapshot only watchlist tickers) - Progress logging every 10 tickers with success/fail counts and elapsed time
- End-of-run summary groups failed tickers by exchange
- Updated
docs/02_RUNNING.mdwith new CLI flags and rate-limit behaviour - Updated
docs/06_DATA_SOURCE.mdto reflect sequential processing replacing 6-concurrent-worker approach
[1.3.3] - 2026-05-10
Fixed
scripts/seed.tsnow seeds an idempotent sample trade (1 AAPL BUY, 10 @ $150 on 2026-05-01) intoportfolio_trades. Previously, the seed only populatedwatchlist; against a fresh production Turso database this surfaced as "0 rows inportfolio_trades" after a successful seed because there was no insert code. Locally the bug was masked because the Portfolio UI was used to manuallyLog a tradeduring development, which wrote to the local SQLite file.- Idempotency is keyed on
(ticker, side, tradeDate)— a re-run reports0 inserted, 1 updatedand never duplicates the row. - Added explicit per-trade logging (
Inserting trade: …/Trade inserted, id=…/Trade already existed (id=…); refreshed in place) and full-stack error reporting on failure, so any future Turso insert miss won't be silent.
[1.3.2] - 2026-05-09
Fixed
- Removed unused
exportTradesToCsvimport incsv-import-modal.tsx(lint error blocking build) - Removed unused
routervariable anduseRouterimport inscreener-view.tsx(lint error blocking build)
[1.3.1] - 2026-05-09
Changed
- BREAKING: Replaced
better-sqlite3(synchronous) with@libsql/client(async) as the sole database driver - All ~30 DB call sites converted from synchronous to async (
await) - Database access changed from direct
dbexport togetDb()async factory function - CLI scripts (
seed.ts,snapshot.ts) wrapped in async IIFE - CSV bulk import now uses proper
db.transaction(async (tx) => {...})for atomic inserts getSettings(),saveSettings(),getBaseCurrency(),saveBaseCurrency()are now async functions returning Promises- Deploy docs updated to reflect single-driver architecture and include seed/migration instructions
Removed
better-sqlite3dependency (was^12.9.0)@types/better-sqlite3dev dependency (was^7.6.13)- Synchronous
dbProxy export fromsrc/lib/db/index.ts
Fixed
- Portfolio CSV import now uses proper libsql transaction for atomicity (previously abandoned transactions due to Drizzle/better-sqlite3 API mismatch)
[1.3.0] - 2026-05-09
Added
- CSV portfolio import/export with auto-detection for 5 broker formats (GrahamScreener, Stake, CommSec, Interactive Brokers, Zerodha) and manual column-mapping fallback
papaparsedependency for CSV parsing and generation- Bulk import API endpoint (
POST /api/portfolio/import) with validation - Sample CSV template at
/public/sample-template.csv - Configurable valuation settings: WACC, tax rate, AAA bond yield, base multiplier, growth cap
- Settings table in SQLite schema for persisting user preferences
- Settings API (
GET/PUT /api/settings) and settings drawer UI with gear icon in sidebar - Screener preset URLs: filters encoded in URL query params, shallow routing, "Share link" button
- Multi-currency portfolio: base currency selector (USD/AUD/INR/GBP/EUR), FX rate fetching from Yahoo with 24h cache, hardcoded fallback rates, converted KPI values
- FX API endpoint (
GET/PUT /api/fx) for rate fetching and base currency switching - FX fixture data for demo mode (
fixtures/fx-rates.json) @libsql/clientdependency (Turso compatibility — ready for async migration)vercel.jsonwithsyd1region for Vercel deployment/docs/12_DEPLOY.md— complete Turso + Vercel deployment guide
Changed
epvPerShare()andgrahamGrowth()now accept optionalValuationSettingsoverride parametercomputeValuations()passes settings through to EPV and Graham Growth formulas- Stock detail page reads valuation settings from DB before computing valuations
- Chart colours now use CSS variables (
--chart-negative,--chart-benchmark) for theme-aware rendering - Portfolio KPI cards display values in user's selected base currency
- Screener page wrapped in
<Suspense>boundary foruseSearchParams()compatibility
Fixed
- Benchmark chart line used hardcoded
hsl(220 90% 70%)— now uses--chart-benchmarkCSS variable - Price chart negative colour used hardcoded
hsl(0 80% 60%)— now uses--chart-negativeCSS variable
[1.2.1] - 2026-05-09
Changed
- Rebranded from ValueLens to GrahamScreener following registration of grahamscreener.com
- All user-facing strings, page titles, metadata, and documentation updated
package.jsonname field changed tograhamscreener- Tagline updated to "Graham-discipline value screener for ASX, BSE, NSE, and US equities"
- Project folder structure and database filename unchanged
- Historical decision/changelog/report entries preserved with rebrand note prepended
[1.2.0] - 2026-05-09
Added
- In-app documentation viewer at
/docswith sidebar navigation, prev/next links, and markdown rendering via react-markdown + remark-gfm + rehype-highlight DocRendererclient component (src/components/docs/doc-renderer.tsx) with syntax highlighting and dark-theme styling- Demo mode:
npm run dev:demoserves fixture data from/fixtures— no Yahoo Finance dependency - 32 fixture JSON files covering 5 seeded tickers (full quote + chart) plus screener data for 4 exchanges
src/lib/demo.tsutility module:isDemoMode(),demoQuote(),demoChart(),demoSearch(),demoUniverse()- Demo banner component showing amber notification when demo mode is active
- "Docs" link in sidebar navigation (near Health)
- Documentation accuracy audit: 22 checks performed, 6 fixes applied (all related to Health page/route)
docs/AUDIT_LOG_v1.2.md— full audit trail with methodology and results
Changed
- Yahoo client (
src/lib/yahoo/client.ts) now checksisDemoMode()before making network calls - Screener API route serves fixture data when in demo mode
- Health API route includes
demoModeflag in response - Updated
00_START_HERE.mdwith docs viewer and demo mode entries - Updated
04_ARCHITECTURE.mdwith health route in folder structure and diagram
Fixed
- Added missing
/api/healthto API routes table in02_RUNNING.md - Added missing
/healthto URL map in02_RUNNING.md - Added missing
health/route.tsandhealth/page.tsxto folder tree in04_ARCHITECTURE.md
[1.1.0] - 2026-05-09
Added
- Complete documentation suite: 12 files in
/docscovering setup, features, architecture, formulas, data sources, screener guide, troubleshooting, and roadmap docs/00_START_HERE.md— single entry point with quick-start and doc indexdocs/05_FORMULAS.md— every valuation formula with math, source, code path, and worked examplesdocs/06_DATA_SOURCE.md— Yahoo endpoints, rate limits, cache TTLs, and 3 alternative data providersdocs/08_SCREENER_GUIDE.md— full 200-ticker universe table with names and sectors, plus Graham preset definitionsdocs/09_TROUBLESHOOTING.md— 20+ symptom/cause/fix entriesdocs/10_ROADMAP.md— prioritised feature backlog with effort estimates and API cost analysis- Defensive Investor and Enterprising Investor screener presets (Graham Ch. 14 & 15 thresholds)
TEST_RESULTS.md— smoke test results from Cowork verification passCOWORK_REPORT.md— full session report
Changed
- Updated
README.mdto point to/docs/00_START_HERE.mdas canonical entry point - Appended "Cowork Fixes" section to
DECISIONS.md
Fixed
- Documented WAL checkpoint requirement for clean DB state after build
[1.0.0] - 2026-05-09
Added
- Multi-exchange value-investing screener covering ASX, BSE, NSE, and US equities
- Stock detail page with live Yahoo Finance data, price charts (1M–Max), and fundamentals table
- Four valuation models: Graham Number, NCAV per share, EPV per share, Graham Growth formula
- Margin-of-safety bar chart comparing current price to each intrinsic value estimate
- Colour-coded MoS indicators (green/amber/red) with Graham-aligned thresholds
- Watchlist with thesis, target buy price, stop loss, and position size fields
- Portfolio trade log with FIFO cost-basis computation and realised P&L tracking
- Screener with 5 Graham-style filters across 200 large-caps (50 per exchange)
- Three screener presets: Defensive (Graham), Income, Quality + Cheap
- Snapshot system for batch-fetching fundamentals into SQLite cache
- CLI tools:
npm run seed(watchlist seeder),npm run snapshot(batch fundamentals fetch) - Command palette (Cmd+K) with global ticker search and page navigation
- Keyboard shortcuts:
g d/s/w/pfor Dashboard/Screener/Watchlist/Portfolio - Yahoo Finance session manager with cookie + crumb bootstrap and retry logic
- SQLite database with WAL mode via better-sqlite3 + Drizzle ORM
- Dark mode default with light mode toggle (next-themes)
- Glassmorphism UI inspired by Linear/Vercel/Stripe
- Responsive layout: sidebar nav on desktop, bottom nav on mobile
- Empty states with SVG illustrations and seed CTAs
- Zero environment variables — fully self-contained local app
Last updated: 2026-05-10 by Claude Cowork