From e916df5dcdb402932bdfa2b086e5430a31b9fd1b Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Mon, 14 Sep 2026 13:37:33 -0700 Subject: [PATCH 1/2] feat(tools): render the WCM social card from the shared template tools/render-og-card.py rendered only the agentrust-io.com card. It now holds named cards in one template, and `wcm --out PATH` renders the Weight Custody Manifest card that the WCM docs site commits as docs/assets/og.png. The chain row underlines the card's own step. The /wcm/ redirect stub points its Open Graph tags at that card, 1200x630, instead of the 1731x909 launch image that still read agentrust.io. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_013aK3gVWzNdcM3hZ2o2awK2 --- tools/render-og-card.py | 82 +++++++++++++++++++++++++++++++---------- wcm/index.html | 15 ++++---- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/tools/render-og-card.py b/tools/render-og-card.py index 06f0aec..1b82966 100644 --- a/tools/render-og-card.py +++ b/tools/render-og-card.py @@ -1,17 +1,24 @@ -"""Render /og.png, the 1200x630 social card for agentrust-io.com, with headless Chrome. +"""Render AgenTrust social cards (1200x630 PNG) with headless Chrome. - python tools/render-og-card.py # writes og.png + python tools/render-og-card.py # agentrust-io.com, writes og.png + python tools/render-og-card.py wcm --out PATH # wcm.agentrust-io.com card CHROME=/path/to/chrome python tools/render-og-card.py -The card is HTML so its words can change with an edit, not a lost design file. -It lives here rather than as a page because every *.html in the repository is a -published page to check-site.py and the sitemap. Keep the words in step with the -homepage hero and with the og:image:alt text the pages use; colours are the brand -navy and red from brand/BRAND.md. +Every card is the same HTML template with different words, so the family stays +consistent and the next wording change is an edit and one command, not a lost +design file. Cards for the docs hosts are committed in their own repositories +(the WCM card lives at docs/assets/og.png in weight-custody-manifest), so pass +--out for those. + +The template lives here rather than as a page because every *.html in this +repository is a published page to check-site.py and the sitemap. Keep the words +in step with each site's hero and its og:image:alt; colours are the brand navy +and red from brand/BRAND.md. No --check: a PNG rendered on another machine differs in font hinting, so commit the rendered file and review it by eye. """ +import argparse import os from pathlib import Path import shutil @@ -20,12 +27,26 @@ import tempfile ROOT = Path(__file__).resolve().parents[1] - -EYEBROW = 'Open specifications for verifiable AI' -TITLE = 'AgenTrust' -LEAD = 'Prove what your AI ran, and what it did.' CHAIN = ['Weights', 'Agent', 'Actions', 'Evidence'] -DOMAIN = 'agentrust-io.com' + +CARDS = { + 'hub': { + 'out': ROOT / 'og.png', + 'eyebrow': 'Open specifications for verifiable AI', + 'title': 'AgenTrust', + 'lead': 'Prove what your AI ran, and what it did.', + 'step': None, + 'domain': 'agentrust-io.com', + }, + 'wcm': { + 'out': None, + 'eyebrow': 'An AgenTrust open specification', + 'title': 'Weight Custody Manifest', + 'lead': 'Release model weights only to an attested runtime.', + 'step': 1, + 'domain': 'wcm.agentrust-io.com', + }, +} CARD = """ @@ -36,11 +57,12 @@ body {{ background: #15294B; color: #FFFEFA; position: relative; font-family: Inter, "Segoe UI", ui-sans-serif, sans-serif; }} .wrap {{ position: absolute; left: 90px; right: 90px; top: 84px; }} .eyebrow {{ font-size: 25px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #9FB3D1; }} - h1 {{ font-size: 118px; font-weight: 700; margin: 26px 0 0; letter-spacing: -.01em; line-height: 1; }} + h1 {{ font-size: {title_size}px; font-weight: 700; margin: 26px 0 0; letter-spacing: -.01em; line-height: 1; }} .bar {{ width: 120px; height: 9px; background: #B91C1C; margin: 38px 0 34px; }} .lead {{ font-size: 44px; line-height: 1.2; color: #E9E4D8; margin: 0; }} .chain {{ margin-top: 30px; font-size: 27px; color: #9FB3D1; letter-spacing: .01em; }} .chain b {{ color: #FFFEFA; font-weight: 600; }} + .chain .on {{ color: #FFFEFA; border-bottom: 3px solid #B91C1C; padding-bottom: 2px; }} .domain {{ position: absolute; left: 90px; bottom: 46px; font-size: 27px; font-weight: 700; color: #9FB3D1; }} .foot {{ position: absolute; left: 0; right: 0; bottom: 0; height: 14px; background: #B91C1C; }} @@ -73,17 +95,37 @@ def chrome(): raise SystemExit('Chrome not found. Set CHROME to its path.') +def chain_html(step): + parts = [] + for i, name in enumerate(CHAIN, 1): + item = f'{i:02d} {name}' + parts.append(f'{item}' if i == step else item) + return '  ·  '.join(parts) + + def main(): - chain = '  ·  '.join(f'{i:02d} {step}' for i, step in enumerate(CHAIN, 1)) - html = CARD.format(eyebrow=EYEBROW, title=TITLE, lead=LEAD, chain=chain, domain=DOMAIN) + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument('card', nargs='?', default='hub', choices=sorted(CARDS)) + parser.add_argument('--out', type=Path, help='where to write the PNG (required for docs-host cards)') + args = parser.parse_args() + + card = CARDS[args.card] + out = args.out or card['out'] + if out is None: + raise SystemExit(f'the {args.card} card is committed in its own repository: pass --out') + html = CARD.format( + eyebrow=card['eyebrow'], title=card['title'], lead=card['lead'], domain=card['domain'], + chain=chain_html(card['step']), + # One line at 1020px wide: long names step down rather than wrap. + title_size=118 if len(card['title']) <= 12 else 84, + ) with tempfile.TemporaryDirectory() as tmp: - card = Path(tmp) / 'og-card.html' - card.write_text(html, encoding='utf-8') - out = ROOT / 'og.png' + page = Path(tmp) / 'og-card.html' + page.write_text(html, encoding='utf-8') subprocess.run([chrome(), '--headless=new', '--disable-gpu', '--hide-scrollbars', '--force-device-scale-factor=1', '--window-size=1200,630', - f'--screenshot={out}', card.as_uri()], check=True, capture_output=True) - print(f'wrote {out.relative_to(ROOT)}') + f'--screenshot={Path(out).resolve()}', page.as_uri()], check=True, capture_output=True) + print(f'wrote {out}') if __name__ == '__main__': diff --git a/wcm/index.html b/wcm/index.html index 9cc838f..aac55ae 100644 --- a/wcm/index.html +++ b/wcm/index.html @@ -20,7 +20,8 @@ crawler that lands on an old share resolves to the same card. og-launch.png stays in this directory. Cards cached against - agentrust-io.com/wcm/og-launch.png still resolve, and nothing rewrites that path. + agentrust-io.com/wcm/og-launch.png still resolve, but new shares use the docs + site's card, rendered by tools/render-og-card.py wcm. --> @@ -32,22 +33,22 @@ - - - - + + + + - + - +