GMI · TECHNOLOGY OBSERVATORY // ALL SYSTEMS NOMINAL
ENGINEERED BY LEOPARD DATA

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

THE PIPELINE
rendering diagram…
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]
Cards are rendered in the same batch run that grades the ticker and writes the page — page and card publish atomically in one deploy, so an og:image can never point at a missing or stale card.

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).

CACHE-PROOF SERVING
rendering diagram…
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 --> F
Dated filenames make every regrade a NEW url — the social platforms' aggressive preview caches are sidestepped rather than fought. Serving is static files; a viral moment costs bandwidth pennies, not compute.

Pre-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.