Skip to content

Commit e916df5

Browse files
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) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013aK3gVWzNdcM3hZ2o2awK2
1 parent e0b9eb5 commit e916df5

2 files changed

Lines changed: 70 additions & 27 deletions

File tree

‎tools/render-og-card.py‎

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
1-
"""Render /og.png, the 1200x630 social card for agentrust-io.com, with headless Chrome.
1+
"""Render AgenTrust social cards (1200x630 PNG) with headless Chrome.
22
3-
python tools/render-og-card.py # writes og.png
3+
python tools/render-og-card.py # agentrust-io.com, writes og.png
4+
python tools/render-og-card.py wcm --out PATH # wcm.agentrust-io.com card
45
CHROME=/path/to/chrome python tools/render-og-card.py
56
6-
The card is HTML so its words can change with an edit, not a lost design file.
7-
It lives here rather than as a page because every *.html in the repository is a
8-
published page to check-site.py and the sitemap. Keep the words in step with the
9-
homepage hero and with the og:image:alt text the pages use; colours are the brand
10-
navy and red from brand/BRAND.md.
7+
Every card is the same HTML template with different words, so the family stays
8+
consistent and the next wording change is an edit and one command, not a lost
9+
design file. Cards for the docs hosts are committed in their own repositories
10+
(the WCM card lives at docs/assets/og.png in weight-custody-manifest), so pass
11+
--out for those.
12+
13+
The template lives here rather than as a page because every *.html in this
14+
repository is a published page to check-site.py and the sitemap. Keep the words
15+
in step with each site's hero and its og:image:alt; colours are the brand navy
16+
and red from brand/BRAND.md.
1117
1218
No --check: a PNG rendered on another machine differs in font hinting, so commit
1319
the rendered file and review it by eye.
1420
"""
21+
import argparse
1522
import os
1623
from pathlib import Path
1724
import shutil
@@ -20,12 +27,26 @@
2027
import tempfile
2128

2229
ROOT = Path(__file__).resolve().parents[1]
23-
24-
EYEBROW = 'Open specifications for verifiable AI'
25-
TITLE = 'AgenTrust'
26-
LEAD = 'Prove what your AI ran, and what it did.'
2730
CHAIN = ['Weights', 'Agent', 'Actions', 'Evidence']
28-
DOMAIN = 'agentrust-io.com'
31+
32+
CARDS = {
33+
'hub': {
34+
'out': ROOT / 'og.png',
35+
'eyebrow': 'Open specifications for verifiable AI',
36+
'title': 'AgenTrust',
37+
'lead': 'Prove what your AI ran, and what it did.',
38+
'step': None,
39+
'domain': 'agentrust-io.com',
40+
},
41+
'wcm': {
42+
'out': None,
43+
'eyebrow': 'An AgenTrust open specification',
44+
'title': 'Weight Custody Manifest',
45+
'lead': 'Release model weights only to an attested runtime.',
46+
'step': 1,
47+
'domain': 'wcm.agentrust-io.com',
48+
},
49+
}
2950

3051
CARD = """<!DOCTYPE html>
3152
<html lang="en">
@@ -36,11 +57,12 @@
3657
body {{ background: #15294B; color: #FFFEFA; position: relative; font-family: Inter, "Segoe UI", ui-sans-serif, sans-serif; }}
3758
.wrap {{ position: absolute; left: 90px; right: 90px; top: 84px; }}
3859
.eyebrow {{ font-size: 25px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #9FB3D1; }}
39-
h1 {{ font-size: 118px; font-weight: 700; margin: 26px 0 0; letter-spacing: -.01em; line-height: 1; }}
60+
h1 {{ font-size: {title_size}px; font-weight: 700; margin: 26px 0 0; letter-spacing: -.01em; line-height: 1; }}
4061
.bar {{ width: 120px; height: 9px; background: #B91C1C; margin: 38px 0 34px; }}
4162
.lead {{ font-size: 44px; line-height: 1.2; color: #E9E4D8; margin: 0; }}
4263
.chain {{ margin-top: 30px; font-size: 27px; color: #9FB3D1; letter-spacing: .01em; }}
4364
.chain b {{ color: #FFFEFA; font-weight: 600; }}
65+
.chain .on {{ color: #FFFEFA; border-bottom: 3px solid #B91C1C; padding-bottom: 2px; }}
4466
.domain {{ position: absolute; left: 90px; bottom: 46px; font-size: 27px; font-weight: 700; color: #9FB3D1; }}
4567
.foot {{ position: absolute; left: 0; right: 0; bottom: 0; height: 14px; background: #B91C1C; }}
4668
</style>
@@ -73,17 +95,37 @@ def chrome():
7395
raise SystemExit('Chrome not found. Set CHROME to its path.')
7496

7597

98+
def chain_html(step):
99+
parts = []
100+
for i, name in enumerate(CHAIN, 1):
101+
item = f'<b>{i:02d}</b> {name}'
102+
parts.append(f'<span class="on">{item}</span>' if i == step else item)
103+
return ' &nbsp;·&nbsp; '.join(parts)
104+
105+
76106
def main():
77-
chain = ' &nbsp;·&nbsp; '.join(f'<b>{i:02d}</b> {step}' for i, step in enumerate(CHAIN, 1))
78-
html = CARD.format(eyebrow=EYEBROW, title=TITLE, lead=LEAD, chain=chain, domain=DOMAIN)
107+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
108+
parser.add_argument('card', nargs='?', default='hub', choices=sorted(CARDS))
109+
parser.add_argument('--out', type=Path, help='where to write the PNG (required for docs-host cards)')
110+
args = parser.parse_args()
111+
112+
card = CARDS[args.card]
113+
out = args.out or card['out']
114+
if out is None:
115+
raise SystemExit(f'the {args.card} card is committed in its own repository: pass --out')
116+
html = CARD.format(
117+
eyebrow=card['eyebrow'], title=card['title'], lead=card['lead'], domain=card['domain'],
118+
chain=chain_html(card['step']),
119+
# One line at 1020px wide: long names step down rather than wrap.
120+
title_size=118 if len(card['title']) <= 12 else 84,
121+
)
79122
with tempfile.TemporaryDirectory() as tmp:
80-
card = Path(tmp) / 'og-card.html'
81-
card.write_text(html, encoding='utf-8')
82-
out = ROOT / 'og.png'
123+
page = Path(tmp) / 'og-card.html'
124+
page.write_text(html, encoding='utf-8')
83125
subprocess.run([chrome(), '--headless=new', '--disable-gpu', '--hide-scrollbars',
84126
'--force-device-scale-factor=1', '--window-size=1200,630',
85-
f'--screenshot={out}', card.as_uri()], check=True, capture_output=True)
86-
print(f'wrote {out.relative_to(ROOT)}')
127+
f'--screenshot={Path(out).resolve()}', page.as_uri()], check=True, capture_output=True)
128+
print(f'wrote {out}')
87129

88130

89131
if __name__ == '__main__':

‎wcm/index.html‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
crawler that lands on an old share resolves to the same card.
2121
2222
og-launch.png stays in this directory. Cards cached against
23-
agentrust-io.com/wcm/og-launch.png still resolve, and nothing rewrites that path.
23+
agentrust-io.com/wcm/og-launch.png still resolve, but new shares use the docs
24+
site's card, rendered by tools/render-og-card.py wcm.
2425
-->
2526

2627
<link rel="canonical" href="https://wcm.agentrust-io.com/">
@@ -32,22 +33,22 @@
3233
<meta property="og:description" content="An open, pre-1.0 specification for releasing model weights only to an approved, attested runtime, with the guarantee boundary stated plainly.">
3334
<meta property="og:url" content="https://wcm.agentrust-io.com/">
3435
<meta property="og:locale" content="en_US">
35-
<meta property="og:image" content="https://agentrust-io.com/wcm/og-launch.png">
36-
<meta property="og:image:width" content="1731">
37-
<meta property="og:image:height" content="909">
38-
<meta property="og:image:alt" content="Weight Custody Manifest: Release, Attest, Revoke">
36+
<meta property="og:image" content="https://wcm.agentrust-io.com/assets/og.png">
37+
<meta property="og:image:width" content="1200">
38+
<meta property="og:image:height" content="630">
39+
<meta property="og:image:alt" content="Weight Custody Manifest: release model weights only to an attested runtime">
3940

4041
<meta name="twitter:card" content="summary_large_image">
4142
<meta name="twitter:title" content="Weight Custody Manifest">
4243
<meta name="twitter:description" content="An open, pre-1.0 specification for releasing model weights only to an approved, attested runtime, with the guarantee boundary stated plainly.">
43-
<meta name="twitter:image" content="https://agentrust-io.com/wcm/og-launch.png">
44+
<meta name="twitter:image" content="https://wcm.agentrust-io.com/assets/og.png">
4445

4546
<link rel="icon" href="/favicon.ico" sizes="any">
4647
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
4748
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
4849
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
4950

50-
<link rel="stylesheet" href="/design-system.css?v=18">
51+
<link rel="stylesheet" href="/design-system.css?v=21">
5152
<script>
5253
// replace() rather than assign(): Back should return to wherever the reader
5354
// came from, not bounce them through this page again.

0 commit comments

Comments
 (0)