Auto-generate HTML report + modern, research-grade report redesign - #23
Merged
Conversation
Follow-up to the quality-aware scoring change (#21), which shipped without user-facing docs. - CHANGELOG: add an [Unreleased] Changed entry covering the quality factor, the net-cash reward, the Debt -> Balance Sheet rename, the reweighting, and the preserved rank order. - README: update the rating-buckets table to the shipped weights (15/17/10/11/10/12/10/5/5/5, +5 ESG) with the Balance Sheet bucket, and add a short note explaining quality-aware valuation and the net-cash effect.
Deliver the analysis as a document automatically and make it read as professional equity research in both HTML and PDF. Auto-HTML - `investo analyze` writes a self-contained HTML note by default (--no-html to skip; announced on stderr so --json stays pipeable). - The MCP `analyze_company` tool writes one too, returning html_report_path (plus generated_at, investo_version, html_bytes); emit_html=false opts out. The tool is now correctly marked as a file writer. Modern redesign (render/css.py, render/html.py) - Design-token system: spacing scale, radii, shadows and a semantic status palette in :root, consumed everywhere. - New furniture: masthead rating block, KPI cards, colour status badges from one shared mapping, card-framed zebra tables, a table of contents, a colophon, <meta description> and accessible table captions. - Removes the heavy letter-spacing that made PDF text render and copy as "I N V E S T O". Research-grade PDF (print CSS + export.py) - Per-page running header/footer via a repeating thead/tfoot group, replacing fixed-position furniture that rendered inverted and clipped body text on full pages. - Tables and charts fit the page, table headers repeat, colours print, cover page break. Tests updated and added (auto-HTML on CLI/MCP, badges/cards/TOC, captions, a no-BUY/SELL guard); all 284 pass.
- mypy: annotate the KPI growth-signal local as str | None (the two branches assign different Literal types, which mypy rejected as an implicit narrow). - ruff: pre-existing nits the newer CI linter now flags — sort the finutils import members and rewrite a test-helper dict() call as a literal.
This was referenced Jul 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
The report (HTML on screen, PDF in print) is Investo's user-facing deliverable, and three things fell short: it was never produced automatically, the look was a deliberately austere print note, and the PDF was not research-grade (uppercase chrome tracked so heavily it rendered/copied as
I N V E S T O, plus a fragile running header/footer and tables that could overflow). This PR fixes all three.Changes
Auto-generate the HTML report
investo analyze <name>now writes a self-contained HTML note by default (namedinvesto-<SYMBOL>-<date>.htmlin the cwd). The notice goes to stderr so--jsonstays pipeable.--no-htmlopts out;--html FILE/--pdfstill override, and an explicit artifact suppresses the duplicate auto-HTML.analyze_companywrites an HTML note too and returnshtml_report_path(plusgenerated_at,investo_version,html_bytes);emit_html=falseopts out. The tool is now correctly annotated as a file writer.Modern, visual redesign (
render/css.py,render/html.py):root: spacing scale, radii, shadows and a semantic status palette, consumed everywhere (no hardcoded colour at a call site).<meta name="description">, and accessible table<caption>s.I N V E S T Oartifact.Research-grade PDF (print CSS +
export.py)<thead>/<tfoot>group — the reliable technique in Chromium — replacingposition:fixedfurniture that rendered inverted and clipped body text on full pages.prefer_css_page_sizeso the document's@pagerule owns size/margins.Verification
pytest -q— 284 pass (updated the stale "no coloured pill" and read-only-tool tests and the CLI tests; added coverage for auto-HTML on CLI + MCP, badges/cards/TOC, captions, and a no-BUY/SELL guard).RELIANCE.NSend-to-end to HTML and an 11-page PDF via headless Chrome and inspected every representative page (cover, thesis/rating, tables): header/footer correct with no overlap, tables fit, sensible page breaks, colours correct.analyze_companypath returns a validhtml_report_pathand thatemit_html=falsewrites nothing.Note
The stylesheet's original docstring codified "no cards, no coloured pills, status as typography." This PR deliberately reverses that toward a modern, visual look (docstring and the CSS-substring tests updated to match).