## Context This is a greenfield Next.js + TypeScript web app that turns a single NYSE/Nasdaq ticker into a thorough, agent-written evaluation (valuation, macro, entry/exit, stop-loss). The target output shape is captured concretely in `examples/de-reentry-2026-08-21.md` and encoded as requirements in the four spec files. Constraints: bring-your-own API keys, free-tier market data by default, rate limits, and no persistence in v1. The design must keep data-provider and model-provider choices swappable so cost/quality decisions don't ripple into the evaluation or UI code. ## Goals / Non-Goals **Goals:** - One typed pipeline: `resolve ticker → fetch data → compute evaluation → agent narrative`. - Provider-agnostic boundaries: `DataProvider` and `AnalysisAgent` interfaces are the only seams consumers see. - Deterministic, testable evaluation math separated from the non-deterministic agent prose. The structured evaluation must stand on its own even with no agent key. - Secrets stay server-side only. **Non-Goals:** - Persistence, accounts, watchlists, alerts, real-time streaming, backtesting. - Multi-ticker dashboards. (Deferred to later changes.) - Trade execution or brokerage integration. ## Decisions ### D1 — App structure: Next.js App Router, server-side data/agent calls Route handlers (or server actions) under `app/api/*` run the pipeline server-side; the client is a thin report view. Rationale: keeps API keys off the browser (satisfies the app-config spec), and lets data + agent calls share one request. Alternative considered: client-side fetching — rejected because it would leak keys and duplicate rate-limit handling. ### D2 — `DataProvider` interface with a free default A single interface returns normalized models: `CompanyProfile`, `Fundamentals` (TTM + per-FY, optional segments), `PriceHistory`, `TechnicalContext` (computed), `AnalystCoverage`, `Estimates`. Default implementation targets a free tier (candidate: Financial Modeling Prep or Finnhub; final pick during tasks). Each model carries an `asOf` timestamp and `delayed|realtime` flag. Alternative: code directly against one vendor — rejected for lock-in and testability. ### D3 — Technical indicators computed in-house, not taken from the provider Moving averages (20/50/200), swing highs/lows, 52-week distance, volume multiple, and realized volatility are computed from OHLCV in a pure `technicals` module. Rationale: providers expose these inconsistently; in-house math is deterministic and unit-testable, and it is the same input the entry/exit/stop logic needs. ### D4 — Evaluation engine is pure and provider-free `evaluate(inputs) → Evaluation` is a pure function over normalized data. It emits a typed `Evaluation` object (one field per spec section) with explicit `unavailable` markers — never fabricated or zero-filled values. All entry/exit/ stop levels, implied multiples at each level, and valuation reasoning are computed here. Rationale: the spec requires the structured object to be consumable without the agent, and pure functions make the many numeric requirements testable. ### D5 — `AnalysisAgent` interface; Claude default via `@anthropic-ai/sdk` The agent takes the `Evaluation` object and returns narrative sections. Default impl calls a current Claude model, instructed to use ONLY the supplied figures, preserve any flagged caveats/discrepancies, and include the disclaimer. Structured output (tool/JSON schema) is preferred so the UI can render narrative per section. Alternative: free-text prompt with the raw data — rejected because grounding and per-section rendering are weaker. When no key is set, the pipeline returns the `Evaluation` with `narrative: unavailable`. ### D6 — Typed results end-to-end; errors are values Data, evaluation, and agent layers return typed results/errors (not thrown exceptions across boundaries). A data failure, rate-limit, or agent failure each degrade to a partial result the UI can render. Rationale: the specs require honest partial states and readable errors. ### D7 — Caching and rate limiting at the provider boundary An in-memory (v1) cache keyed by ticker+dataset with short TTLs sits inside the provider layer, with retry/backoff for transient/rate-limit errors. Rationale: free tiers are tightly limited; caching one evaluation's repeated reads avoids burning quota. Persistence-backed cache is a later change. ## Risks / Trade-offs - [Free-tier data is delayed/incomplete — segments, estimates, or history may be missing] → Normalized models mark fields unavailable; evaluation and UI render honestly; provider is swappable for a paid tier without consumer changes. - [Agent may hallucinate numbers] → Agent receives only the structured object, is instructed to cite nothing outside it, and prose is rendered alongside the computed tables so drift is visible; consider a post-check that every cited figure exists in the input. - [Rate limits during development/demo] → Caching + backoff; a fixture/mock provider for tests and offline work. - [Numeric correctness of valuation/technical math] → Pure modules with unit tests seeded from the DE example's known figures. - [Not financial advice / liability] → Mandatory disclaimer enforced in both the evaluation object and the agent narrative. ## Migration Plan Greenfield — no data migration. Deployment: run locally (`localhost:3000`) with `.env.local` holding the market-data and Anthropic keys. Rollout is incremental by capability (see tasks): scaffold → data layer + technicals → evaluation engine → agent → UI. Rollback is trivial (no persisted state). Keys are provided via env; absence degrades gracefully rather than failing the build. ## Open Questions - Which free market-data provider is the default (coverage of segments + forward estimates varies materially)? Resolve early in tasks by spiking 2 providers on `DE`. - Source of options-implied expected move and forward consensus EPS on free tiers — may need a secondary source or graceful omission. - Preferred charting library (Recharts vs. lightweight-charts) for overlaying entry/exit/stop markers on price history. - Whether to add an automated "every cited figure exists in inputs" guard on agent output in v1 or defer to a later hardening change.