Spaces:
Running
Running
| # Spec traceability | |
| Every requirement in the build spec β the file that implements it β the test that | |
| holds it. Section numbers refer to the *Final Build Spec*. | |
| Legend: **β ** implemented and tested Β· **π** documentation deliverable Β· | |
| **βοΈ** infrastructure, written but not applied (needs the user's own cloud account) | |
| --- | |
| ## Β§1β2 Β· Product and architecture | |
| | # | Requirement | Implementation | Test | | |
| | --- | --- | --- | --- | | |
| | R1 | Product codename & UI logo: AlphaBrief | `apps/web/components/Chrome.tsx` | markup check (AUDIT c) | | |
| | R3 | Demo URL `alphabrief.vercel.app` | `README.md`, `apps/web/vercel.json` | π | | |
| | R4 | `$0` total cost β yfinance + RSS keyless, Claude only | `app/mcp_server/providers.py` | `test_mcp_server.py` | | |
| | R5 | 3 worker agents + supervisor, 5-ticker watchlist | `app/graph/build.py`, `DEFAULT_WATCHLIST` | `test_integration.py` | | |
| | R6 | Wow moment: RUN β live feed β green ticks β gate β brief | `apps/web/components/RunConsole.tsx` | AUDIT (c), (d) | | |
| | R7 | Trigger: Vercel Cron weekdays 07:00 **or** the RUN button | `apps/web/app/api/trigger/route.ts`, `vercel.json` | β route + constant-time cron auth | | |
| | R8 | Supervisor (Haiku 4.5): plans, parallel fan-out, retries once, hard cap 15 + token budget | `app/graph/supervisor.py` | `test_loop_safety.py` (10) | | |
| | R9 | Data agent (Sonnet): `price_history`, `fundamentals`, `compute_metrics` | `app/graph/data_agent.py` | `test_integration.py`, `test_mcp_server.py` | | |
| | R10 | News agent (Sonnet): `fetch_rss_news` β summarise, sentiment β1..1 with reasoning, risk events | `app/graph/news_agent.py` | `test_degradation.py`, `test_security.py` | | |
| | R11 | MCP tool server β standardised, discoverable, reusable by any framework | `app/mcp_server/` + `GET /v1/mcp/tools` | `test_mcp_server.py::TestDiscoverability` | | |
| | R12 | Shared state typed, reducer-merged, no lost updates | `app/graph/state.py` | `test_reducers.py` (10) | | |
| | R13 | Writer (Sonnet 4.6): Pydantic-enforced brief; assembles only, computes nothing | `app/graph/writer.py`, `app/models/brief.py` | `test_brief_schema.py` (25) | | |
| | R14 | Verification node β deterministic, never an LLM; mismatch β one regeneration else HUMAN_REVIEW | `app/graph/verify.py`, `app/graph/recompute.py` | `test_verifier.py` (20) | | |
| | R15 | HITL gate β LangGraph interrupt + checkpointer; Approve/Edit/Reject; never auto-sends | `app/graph/gate.py` | `test_integration.py::TestHumanGate` (5) | | |
| | R16 | Delivery & archive β SMTP email + Neon Postgres + Langfuse trace | `app/services/email.py`, `repository.py`, `core/tracing.py` | β (email send: caveat E-1) | | |
| | R17 | Signature sentence present in the product | `README.md`, `apps/web/components/Chrome.tsx` | markup check | | |
| --- | |
| ## Β§3 Β· Tech stack | |
| | # | Requirement | Implementation | | |
| | --- | --- | --- | | |
| | R18 | LangGraph: typed StateGraph + checkpointer + interrupts; CrewAI/AutoGen named as alternatives | `app/graph/build.py`; README Β§ *Design decisions* | | |
| | R19 | MCP server (Python SDK) wrapping yfinance/RSS/metrics | `app/mcp_server/registry.py` | | |
| | R20 | Model routing by task weight: Haiku supervisor, Sonnet workers/writer | `app/core/claude.py`, `core/settings.py` | | |
| | R21 | yfinance market data + stated production upgrade path | `providers.py`; README Β§ *Production upgrade path* | | |
| | R22 | RSS via feedparser (Yahoo Finance + Google News) | `providers.py` | | |
| | R23 | Verification: pure Python + Pydantic-parsed brief | `verify.py`, `recompute.py` | | |
| | R24 | Neon Postgres (runs, briefs, approvals) + LangGraph checkpointer | `app/models/db.py`, `services/runner.py` | | |
| | R25 | Langfuse traces of every agent step + token spend | `app/core/tracing.py`, `graph/llm.py` | | |
| | R26 | Next.js 15, dark design system, live SSE feed | `apps/web/` | | |
| | R27 | Vercel Cron; n8n named as the alternative used in LedgerLens | `vercel.json`; README Β§ *Design decisions* | | |
| --- | |
| ## Β§4 Β· Zero-cost deployment | |
| | # | Requirement | Where | Status | | |
| | --- | --- | --- | --- | | |
| | R28 | HF Spaces Docker: FastAPI + LangGraph + MCP in one container, port 7860 | `apps/api/Dockerfile` | βοΈ image builds as uid 1000 on 7860 | | |
| | R29 | Vercel Hobby: UI + one-a-day cron | `vercel.json` | βοΈ | | |
| | R31 | Claude cost ~$0.05β0.15/brief via Haiku routing + caps | `settings.py`, `budget.py` | β guard tested | | |
| | R32 | Cache pulls per run; polite rate limits | `providers.py` `ProviderContext` | `test_mcp_server.py::TestCachingAndRateLimiting` | | |
| | R33 | GCP Cloud Run as primary host, with gcloud commands | README; `infra/terraform/` | βοΈ | | |
| | R34 | AWS/Azure verdicts documented | README Β§ *Free deployment map* | π | | |
| | R35 | HF Spaces as the no-card fallback | README; Dockerfile | π | | |
| | R36 | Optional Terraform module: Cloud Run service + secrets | `infra/terraform/{main,variables,outputs}.tf` | βοΈ | | |
| --- | |
| ## Β§5 Β· Repository layout | |
| Matches the spec exactly: `apps/web`, `apps/api/app/{graph,mcp_server,core}`, | |
| `tests/`, `eval/run_eval.py`, `infra/`, `AUDIT.md`, `README.md`, `.env.example`. | |
| `graph/` contains the specified `state.py`, `supervisor.py`, `data_agent.py`, | |
| `news_agent.py`, `writer.py`, `verify.py`, `gate.py` β plus `recompute.py` | |
| (the verifier's independent maths), `compose.py`, `deliver.py`, `build.py`, | |
| `context.py`, `llm.py` and `prompts.py`. | |
| --- | |
| ## Β§6 Β· UI | |
| | Requirement | Implementation | | |
| | --- | --- | | |
| | Dark, high-contrast design system | `apps/web/app/globals.css` `@theme` tokens β **amber phosphor terminal**, see the deviation below | | |
| | Large glowing RUN button | `RunConsole.tsx` β framed terminal key, amber bloom, pulse | | |
| | SSE feed showing dispatches and each MCP tool call with args + timing | `TelemetryFeed.tsx` (verified live: `get_price_history(ticker=AAPL, days=120) 967ms`) | | |
| | Per-ticker completeness bars | `CompletenessRail.tsx` | | |
| | Verification screen, animated per-claim green ticks / red mismatch | `VerificationPanel.tsx` | | |
| | Approval gate: Approve / Edit / Reject resuming the graph | `ApprovalGate.tsx` | | |
| | Archive with cost, latency, iterations per run | `app/archive/page.tsx` | | |
| | `vercel.json` weekday 07:00 cron β `/api/trigger` | `vercel.json` | | |
| --- | |
| ## Β§7 Β· Production quality bar | |
| | Requirement | Implementation | Test | | |
| | --- | --- | --- | | |
| | Hard cap 15 + budget guard aborting with a clear status | `supervisor.py`, `core/budget.py` | `test_forced_loop_terminates`, `test_run_aborts_with_budget_status` | | |
| | Parallel writes merged via reducers | `state.py` | `test_both_agents_writes_survive_one_superstep` | | |
| | DB writes transactional | `services/repository.py` | `test_repository.py` | | |
| | One active run per watchlist by DB constraint | `models/db.py` partial unique index | `TestActiveRunConstraint` (4) | | |
| | Fake ticker / dead network / empty RSS β partial brief, never a crash | `providers.py`, agents, `compose.py` | `test_degradation.py` (12) | | |
| | Verifier recomputes 100% of claims; mismatch β one regeneration then HUMAN_REVIEW | `verify.py` | `test_verifier.py`, `test_demo_mismatch_is_caught_and_escalated` | | |
| | Eval asserts 100% post-verification accuracy over 20 runs | `eval/run_eval.py` | `eval/results.md` | | |
| | MCP tools whitelisted; no arbitrary execution | `registry.py`, `client.py` | `TestToolWhitelist` (10) | | |
| | News text treated as data (injection-safe prompts) | `core/security.py`, `prompts.py` | 8-payload corpus | | |
| | Secrets via env only | `core/settings.py` | `test_no_default_token_is_ever_shipped` | | |
| | Approval endpoints require an auth token | `core/security.py`, `api/routes.py` | `TestApprovalAuth` (4) | | |
| | ruff + mypy + eslint + tsc clean; pytest green; no browser console errors | β | AUDIT (a), (b) | | |
| --- | |
| ## Β§8 Β· Master prompt specifics | |
| | Requirement | Status | | |
| | --- | --- | | |
| | Default watchlist AAPL, MSFT, NVDA, TSLA, AMZN β editable | β `DEFAULT_WATCHLIST`, editable in the console | | |
| | `get_price_history(ticker, days)` | β | | |
| | `get_fundamentals(ticker)` | β | | |
| | `compute_metrics(prices) β {return_30d, volatility, max_drawdown, pe}` | β plus `last_close`, `previous_close`, `change_1d_pct`, and the 30-day baseline date | | |
| | `fetch_rss_news(ticker, limit)` via feedparser | β | | |
| | Typed args, rich docstrings, per-run cache, polite rate limiting | β `test_mcp_server.py` asserts every tool has >120 chars of documentation | | |
| | Agents call tools for ALL numbers; writer computes nothing | β enforced by the schema, not by the prompt | | |
| | State TypedDict with reducer-merged fields | β | | |
| | Retry failed tickers once; cap 15; `BUDGET_ABORT` on breach | β | | |
| | FastAPI 3.12, langgraph, anthropic, mcp, yfinance, feedparser, SQLAlchemy/Neon, Langfuse, auth token, Dockerfile 7860 | β | | |
| | Next.js 15 TS Tailwind Framer Motion; dark tokens; full console | β | | |
| | `eval/run_eval.py`: 20 runs, mock-clock safe, all four metrics, writes `results.md` | β | | |
| | Definition of Done (a)β(f) + `AUDIT.md` with every check PASS | β `AUDIT.md` | | |
| ## Deviations, stated plainly | |
| **1. Visual identity β the spec's shared design system was overridden.** | |
| Spec Β§6 asks for "dark `#0a0e1a`, cyan `#22d3ee`, glass" and Β§3 justifies it: | |
| "your three demos look like one product family β deliberate, professional." | |
| In practice the two sibling demos had landed on *identical* tokens: | |
| | Project | Base | Accent | | |
| | --- | --- | --- | | |
| | LedgerLens | `#0a0e1a` | `#22d3ee` | | |
| | Lexora | `#0a0e1a` | `#22d3ee` | | |
| A third demo on the same palette reads as one template rendered three times, | |
| which costs more credibility than family resemblance buys. On the project | |
| owner's direction, AlphaBrief was rebuilt with its own identity: | |
| | | AlphaBrief | | |
| | --- | --- | | |
| | Base | `#08090b` true black | | |
| | Primary | `#ffb454` amber phosphor | | |
| | Verified | `#35d07f` phosphor green | | |
| | Alert | `#ff5f56` | | |
| | Type | monospace throughout, tabular numerals | | |
| | Motif | trading-floor terminal: ruled columns, CRT scanlines, corner ticks, block meters | | |
| The choice is also thematically right: this is a market instrument, and the | |
| terminal language says so before a word is read. Everything else in Β§6 β the | |
| glowing RUN key, the SSE feed with per-tool args and timings, per-ticker | |
| completeness meters, the animated verification pass, the Approve/Edit/Reject | |
| gate, the archive with cost/latency/iterations β is implemented as specified. | |
| **2. shadcn/ui not used.** The console uses hand-written components on the same | |
| Tailwind token system. shadcn adds a component registry and Radix dependencies | |
| for a surface that is one button, one textarea and three tables; the terminal | |
| aesthetic is bespoke either way. Tailwind and the dark token system are present | |
| as specified. | |
| --- | |
| ## Β§9β10 Β· Resume, demo script, learning path | |
| | Requirement | Where | | |
| | --- | --- | | |
| | Resume bullets with real numbers | `RESUME.md` | | |
| | 90-second demo script | `RESUME.md` Β§ *Demo script* | | |
| | `demo-fault` mode with a fake ticker | `RunMode.DEMO_FAULT` β plus `DEMO_MISMATCH` for the verifier | | |
| | Learning path: state β supervisor β mcp_server β verify β gate β traces | `LEARNING.md` | | |
| | ATS keyword coverage + categorised skills block | `RESUME.md` Β§ *Skills* | | |
| | Terms not claimed (PyTorch, CUDA, Kubernetes, β¦) | `RESUME.md`, `AUDIT.md` | | |