Social Card Pipeline
How every grade GMI computes becomes a self-advertising image — pre-generated marketing cards for 2,400+ tickers, batch-rendered on grade refresh, served as static files, and atomic with the pages that reference them
flowchart TD
REF[Grade refresh job<br/>admin-triggered or earnings-driven] --> B[SeoPageBuilder batch<br/>report VM]
B --> G[Per ticker: market data -> grade<br/>-> snapshot row]
G --> CARD[Render 1200x630 link card<br/>shared card library]
G --> PAGE[Render static HTML page]
CARD --> NAME[Dated filename<br/>TICKER-grade-date.png]
NAME --> OG[Page og:image references<br/>the card from the SAME run]
PAGE --> OG
OG --> BLOB[(Blob container:<br/>pages + cards)]
BLOB --> SWEEP[Stale sweep: superseded<br/>cards deleted each run]
BLOB --> DEPLOY[Existing deploy pipeline<br/>merges into static site]
DEPLOY --> LIVE[app host serves pages +<br/>cards as static files]The Image Travels Further Than the Website
On social platforms and in messengers, the first impression of any link is the preview card — or nothing. GMI turns every computed grade into a bold, data-rich image: the letter grade at a third of the canvas, ticker and company unmistakable, exactly three plain-English driver stats, the grade date printed into the pixels, and the domain watermarked so even a screenshot advertises. The grade drives the palette — an F card reads red and an A card reads green before a single word is read.
One template renders at three sizes: 1200×630 for link previews (X, Facebook, LinkedIn, Slack, iMessage), 1080×1080 for Reddit and feed posts, and 1080×1350 for Instagram portrait. The link size is pre-generated for every ticker; the feed sizes render on demand in the admin app when a human is preparing a post — pre-generating rarely-fetched sizes for 2,400 tickers would triple the batch output for nothing.
One Renderer, Not Four Features
Every card artifact — the ticker cards here, the shared-report Open Graph cards, and any future template (versus cards, portfolio exports) — flows through a single rendering library. It owns the bundled typeface (neither Linux App Service nor the report VM has system fonts), the grade→color system, the GPA-style portfolio-grade math, and the dated filename scheme. A new shareable artifact is a template method in this library, not a new project.
Rendering is pure-managed image drawing — no browser engine, no native graphics stack — so the identical code runs in the API's request path (report cards), the report VM's batch process (ticker cards), and the unit test suite (every size asserted pixel-dimension-exact with a size budget: flat-color cards palette-quantize to ~20–40 KB, which matters when 2,400 of them ride inside the deployment package).
flowchart LR
subgraph Passive[Passive: link previews]
P1[Anyone shares /grade/TICKER] --> C1[Platform crawler fetches page]
C1 --> C2[Reads og:image -> dated card URL]
C2 --> C3[Static PNG served<br/>zero compute]
C3 --> C4[Bold grade card in the feed]
end
subgraph Active[Active: posting cadence]
A1[Admin Marketing Cards page] --> A2[On-demand render:<br/>square / vertical / link]
A2 --> A3[Download -> post where<br/>investors argue]
A3 --> A4[Watermark + date travel<br/>in the pixels]
end
C4 --> F[Free-tier funnel]
A4 --> FPre-Generate, Don't Render on Demand
Social crawlers are impatient and cache aggressively — the wrong architecture here puts a renderer in the request path and quietly rebuilds the compute bill. GMI's cards are batch artifacts: the same refresh run that grades a ticker renders its card, uploads it next to the page HTML, and the existing deployment pipeline ships both as static files. Nothing computes when a card is fetched. X's crawler, Facebook's scraper, and a Reddit hug all hit static bytes.
Cache invalidation — the fight you cannot win against platform preview caches — is designed
out instead: the filename carries ticker, grade, and date
(NVDA-A-minus-2026-08-21.png), so a refreshed grade mints a new URL, and the
page generated in the same run references exactly that URL. A stale-card sweep deletes
superseded files each run so the deployment package never accretes one card per historical
grade.
Cards Are Ammunition — the Admin App Is the Armory
Link previews work passively, but the active half of the strategy is a human posting grade cards where investors already argue. The admin portal's Marketing Cards page renders any graded ticker's card in all three sizes on demand — authenticated preview, one-click download, posting guidance per size. Cards carry their grade date in the pixels, so the workflow is regenerate-and-post, never repost-old-files: a dated grade card in a comment thread is accountable in a way an undated screenshot never is.
The same pattern extends to the mobile app: sharing a personal report offers the actual card image alongside the link — an image in the thread beats a link alone on surfaces where unfurling doesn't happen.