Spaces:
Sleeping
Sleeping
Commit History
-
feat: read eight statement lines already in the downloaded payload
get_all_xbrl_facts pulls the entire us-gaap companyfacts document and roughly 25 concepts were read from it. Deferred revenue, R&D, SG&A, goodwill, intangibles, cash and short-term investments, the current debt portion and operating lease liabilities were all on the wire already. Each one has a named consumer in the analysis-depth roadmap -- deferred revenue leads revenue by a quarter or two, the current debt portion is the maturity wall total_debt collapses. A concept with no consumer does not enter the schema. All eight go through select_extra, so a filer that tags none of them keeps VERIFIED status on its headline metrics.fc9f590 -
feat: guidance carries its source quote and a distinguishable outcome
A null meant three things at once: the company issued no guidance, the model failed, or nobody tried. Alphabet issues no numeric guidance and Apple withdrew formal guidance, so a low extraction rate on those two is the correct reading of their calls -- but sub-project 3 has to tell a company that has always been silent from one that has just gone silent, and a null cannot say which. guidance_status is now PARSED / NO_GUIDANCE_ISSUED / PARSE_ERROR / NOT_ATTEMPTED, and any figure returned without the passage it came from is refused outright. Guidance is an LLM reading of spoken words, so it is DERIVED, and a DERIVED value that cannot show its input does not render.448dc3d -
feat: decompose the EPS headline and read the statements behind it
Two blocks on The Brief, both answering the question the headline raises and never settles: where did the number come from. analytics/eps_bridge.py partitions the year-on-year change in earnings per share into revenue, operating margin, interest, tax rate, share count and a residual carried openly as "other, non-operating". The identity holds by construction and is re-verified per computation; a bridge that fails its own checksum does not render. Five refusals keep it honest -- no prior-year comparable, a period without SEC lineage, a missing input, a diluted share count that moved more than 25% (a split, not dilution), and a residual larger than half the move. That last gate caught a real defect: Alphabet's quarterly EPS rows hold cumulative figures, so the panel withdraws for GOOGL rather than explain 8% of a move and invent the rest. analytics/statement_signals.py adds three figures that are always worth seeing -- incremental margin, two-year stack, cash conversion -- and a list o…bb3ad6b -
feat: read per-product and per-region results from the filing's XBRL instance
companyfacts returns only undimensioned facts, which is why the brief has never had a segment breakdown. The detail is in the inline-XBRL instance document that ships inside the filing itself, where facts carry explicit dimension members (srt:ProductOrServiceAxis, us-gaap:StatementBusinessSegmentsAxis). One fetch per filing yields the quarter and its prior-year comparable, so year-on-year costs no extra request. Two traps that would produce plausible wrong numbers, both covered by tests: - A filing tags subtotals beside detail members (Apple's "Products" equals iPhone + Mac + iPad + Wearables). Plotting both inflates the mix by 72%. The subtotal is found by anchoring on consolidated revenue and taking the subset equal to the excess, so no per-issuer member list is needed. - The structural fallback enumerates subsets, and an early break on an oversized sum is wrong: itertools.combinations is not ordered by sum. analytics/impact.py adds the arithmetic that turns the breakdown …232b6f9
-
feat: report which metrics passed the consensus gate, not just whether any did
_market_expectations_aligned returns True if EITHER revenue or eps aligns, and by design only revenue normally will: basis_mismatch is the documented, expected outcome for eps, because yfinance publishes adjusted estimates against this repo's GAAP reported figure. A future consumer reading the boolean and rendering an actual-versus-consensus EPS line would render a comparison the gate explicitly refused -- a false claim produced by a fail-closed gate working correctly. display_policy gains market_expectations_aligned_metrics: the list of metrics that actually passed, e.g. ["revenue"]. The boolean stays for backward compatibility and is documented as coarse -- "at least one metric aligned", not sufficient authority to render any particular comparison. The provider-flag branch it ORs with is payload-wide and names no metric, so it can set the boolean True over an empty list; the helper docstring says so. Both fail-closed sites gain the key as an empty list rather than leaving it absent…586fba9 -
fix: survive an LFS pointer at data/metrics.db in the daily job
.gitattributes tracks data/*.db under Git LFS and actions/checkout@v4 defaults to lfs: false, so on the CI runner data/metrics.db is a ~130-byte pointer: DB_PATH.exists() is True and sqlite3 then raises "file is not a database". The daily consensus job would traceback every single day, on the one archive whose gaps cannot be backfilled from any public source. Both halves fixed: - the workflow pulls only data/metrics.db from LFS. `lfs: true` on the checkout would fetch ~100 MB of LFS objects daily (80 MB of it data/chroma/chroma.sqlite3, unread by this job) and exhaust GitHub's LFS bandwidth quota within weeks — and an exhausted quota reinstates the pointer, i.e. the bug. - list_tickers() catches sqlite3.DatabaseError and returns [], so the caller's existing "no covered tickers found" path fires with a readable message and exit code 1 instead of an unhandled traceback. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>abda7ee -
feat: add daily consensus capture script
Adds storage.metrics_db.list_tickers() (the app's de facto coverage universe, previously duplicated inline in warm_runtime_cache.py and deploy_check.py) and scripts/snapshot_consensus.py, which iterates it and calls ingestion.analyst.fetch_analyst_estimates per ticker. No data/consensus_snapshots.jsonl is included: a live run against all six covered tickers (AAPL, AMD, GOOGL, MSFT, NVDA, TSLA) wrote zero archive records. fetch_analyst_estimates(ticker) is called without target_period, so every captured payload carries target_period=None, and record_consensus_snapshot's `if not target_period: return 0` guard silently drops every record. See task-4-report.md for the full analysis; this needs a decision before the archive can hold real data.a95bfa1 -
fix: allow distinct resolutions per forecast_id, reject unidentified ones
Dedup for a resolution record now keys on (forecast_id, record_type, resolved_at) instead of (forecast_id, record_type). A corrected or restated actual (e.g. a 10-K/A amending previously reported EPS) arrives at a later resolved_at and must be appended as its own record, not silently dropped as a duplicate of the original resolution — an append-only log cannot lose a prior observation, including a superseded one. A resolution with a missing or empty resolved_at is rejected: it has no stable identity to dedup or order by. append_forecast's dedup key is unchanged. Controller ruling in response to code review finding on storage/forecast_store.py:112-119.b50dfdb -
feat: add append-only JSONL store for consensus snapshots and forecasts
fdbab16
-
fix: don't diff a brief against a same-quarter rerun of itself
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>e90e984 -
fix: close 4 post-merge review gaps in continuity threads
Post-merge fix wave on the whole-branch review of continuity-threads: - Verification: continuity_threads now goes through the same fail-closed filtering (_filter_unverified_claims) and reliability recompute (_collect_facts) as earnings_quality_signals/ between_the_lines, so an unverified/hallucinated thread evidence item is dropped before it can be persisted to data/threads.db or shown on screen, and never keeps the LLM's self-assigned reliability. - storage/threads_db.py: mark_missed and continue_thread are now idempotent per period, so regenerating a brief for the same filing period (a normal Streamlit action) no longer double-counts a missed quarter or duplicates a history row. - Error isolation: continuity_node, the apply_continuity_threads call in synthesis_node, and continuity_view.render() now wrap their new threads_db I/O in try/except and fail gracefully, matching the established pattern elsewhere in the codebase (signals_node, _finalize_synthesis_pr…05863a5 -
fix: revert mark_missed to spec - no synthetic history below stale threshold
Reverts synthetic "carried over" history entries added below the STALE_AFTER_MISSED_PERIODS threshold. Per design spec and brief Step 3, mark_missed should only append a history entry when transitioning to stale. Below threshold it performs a silent UPDATE of missed_count. Also corrects test assertion: history should have 2 entries (create + continue), not 3, since sub-threshold mark_missed doesn't append. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>0a8ebab -
feat: add threads_db persistence layer for continuity threads
Implements thread persistence using SQLite with full audit trail. Tracks named issues (threads) across quarterly regenerations with status transitions (new→ongoing→escalated→resolved/stale) and history of each change. Auto-retires threads to stale after 2 missed consecutive periods. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>30a8229
-
feat: dark mode, market and quarter context on The Brief, faster cold start
UI - Migrate the whole app to a dark palette driven by dashboard/theme.py tokens rather than an overlay, including a registered Plotly template so charts theme globally. Streamlit base set to dark in config.toml. - The Brief now opens with what a memo about a listed company must show: a market band (price and move, market cap, 52-week range with position marker, dividend yield) and the quarter's reported figures (revenue, EPS, gross and operating margin, FCF with deltas). Rows come from the same masked view Financials uses, so a hidden figure stays hidden. - Surface what_changed on The Brief itself; it was only reachable from another screen although it is the first thing the brief promises. - Warn when most facts are unverified instead of showing a thin brief with no explanation. - Landing page explaining each destination, with saved briefs as entry points, for visitors arriving with no ticker. - Company Primer: prose sections rendered as cards rather than dataframes,…3cd8872
-
fix: keep fail-closed per fact but stop zero-verified synthesis from emptying the whole brief
When no AI-generated claim passes deterministic evidence verification, the brief used to be replaced wholesale by an empty _partial_brief skeleton, discarding filing_date, company_name, what_to_watch, coverage, etc. even though those don't depend on LLM verification. Now only the per-fact filtering (unchanged, still fail-closed) empties individual sections, and a plain-language notice + data freshness banner (120-day staleness heuristic) tell the user what's missing instead of hiding the whole report. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>b419b7b
-
feat: add company primer research tab
d1e793b
-
feat: multi-provider LLM support, prominent chat, design pass, and new analytics
- Add agent/llm.py provider factory (Anthropic/OpenAI) with a sidebar model picker so visitors can paste their own API key instead of relying solely on the server's; thread the resulting config through generation, chat, and translation. - Promote Chat to primary nav (was buried under a secondary "Evidence tool" heading) with suggested-question chips and a "sources searched" line. - Fix a pre-existing bug where every nav/CTA color accent silently no-oped (Streamlit exposes st.button(key=...) as a `st-key-{key}` wrapper class, not a `key` attribute) and tighten the palette to three families (emerald brand, violet AI, semantic bull/bear). - Add a "Sources analyzed" provenance card, quarter/annual basis split on the financials grid, and cross-category signal dedup. - Add valuation + peer comparison, next-catalysts, brief-vs-brief delta, and one-page PDF export. - Rename the product from Primer to Amplegest across the app, docs, and the Hugging Face Space (old URL redire…7880373
-
fix: get_translation — exists check before init_db (matches get_brief pattern)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>0e40276 -
feat: storage/briefs_db.py — DB schema migration + translation cache
- init_db(): Add language column migration (PRAGMA table_info check + ALTER TABLE) and new brief_translations table with (ticker, language) composite key - save_brief(): Extract language from brief_dict, add to INSERT/ON CONFLICT, and invalidate stale translations in same connection - get_brief(): Call init_db() first (migrates old DB before SELECT), fetch language column, setdefault in returned dict - Add get_translation(ticker, language) → retrieves cached translations with corrupt-JSON guard - Add save_translation(ticker, language, brief_dict) → stores translations with UTC saved_at timestamp Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>de10312
-
feat: Decision Stack redesign - 9 routes collapse into 4 (Verdict / Signals / Financials / Chat)
Every datum now has exactly one home; everywhere else references, never re-renders. - Verdict: one-screen decision view - thesis, reconciled Print panel (actual vs consensus EPS/rev, D1/D5, revisions, surprise+streak, sentiment - one source of truth per number with provenance), top-2 bull/bear asymmetry, The Tell, top-3 watch items, reasoning trace. - Signals (new): all qualitative content (tells, tensions, language deltas, changes, risks, quality, points, commentary, notes, news) normalized by signal_feed.py into one ranked stream, filtered with st.pills (type/stance/source/significance), rendered by a single signal_card component; one global semantic search replaces the three per-tab boxes; on-demand news moved here. - Financials (new): KPI snapshot, 5 chart tabs (adds Capital Allocation: capex/dividends/buybacks vs FCF, share count, tax rate), guidance bridge (scorecard + prior guide -> actual -> new guide; removes the detected-by-rule regex implementation lea…bd2c14a -
chore: checkpoint sidebar nav + i18n work-in-progress before Decision Stack redesign
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>e6496c0
-
fix: remove redundant mkdir, guard corrupt JSON, tighten list_tickers sort test
8b9291f -
feat: add briefs_db — persist last brief per ticker for portfolio Radar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>f97bc73 -
fix: chronological sort of periods in sections_db (Q12027 before Q32026)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>8dbbf97 -
feat: Analyst Edge layer — deterministic text delta signals (Phase 0+1)
Adds a signal engine that computes verbatim filing text changes across consecutive periods and injects them into the LangGraph pipeline before the LLM loop, so the agent investigates signals and the synthesis cites exact before→after fragments rather than summarizing what it already knows. Phase 0 — foundation: - storage/sections_db.py: persist raw MD&A, Risk Factors, and transcript text keyed by (ticker, period, section); populated at ingest time - ingest.py: call upsert_section for each text block after edgar fetch - analysis/signals.py: QuarterDelta Pydantic model (kind, before_text, after_text, computed_metric, significance) shared across all modules - agent/graph.py: signals_node as new entry point; edge_signals state key; attach_edge_signals() called in synthesis_node after apply_reliability() - agent/post_synthesis.py: attach_edge_signals() writes authoritative computed data — LLM cannot fabricate signal numbers - agent/schemas.py: BriefOutput.quarter_deltas optional fi…559c2ff
-
fix: background generation, parallel tools, prompt caching, Tavily cache
Bug fixes: - Generation now runs in a background thread so sidebar nav clicks no longer cancel in-flight graph.stream — brief persists regardless - @st.fragment polls the trace every 400ms; auto-navs to Verdict when done - Generate Brief button disabled while generation is running (no double-spawn) - Synthesis streamed via llm_plain.stream() to show in-progress indicator Latency improvements: - Tool calls within a round dispatched in parallel via ThreadPoolExecutor - cache_control: ephemeral on SYSTEM_PROMPT and SYNTHESIS_STRUCTURED_PROMPT reduces input tokens per agent round by ~80-90% after first cache hit - storage/news_cache.py: 1-hour SQLite TTL cache for Tavily news results - Reranker (BAAI/bge-reranker-base) eagerly loaded at app startup to avoid cold-start penalty on the first search_filing/search_transcript call Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2401ce5 -
deploy: Primer app for HF Spaces — clean orphan history
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>35676b4