#!/usr/bin/env python3 """rfx-assert.py — the script that refuses to ship the portfolio page. Runs against templates/page.portfolio.liquid and every snippets/rfx-*.liquid it renders. Exit 0 only when every assertion passes. The page's footer prints this run's counts. Published alongside the page so the gate itself can be read. 2026-09-01.""" import re, sys, json, os, datetime ROOT = os.path.dirname(os.path.abspath(__file__)) TEMPLATE = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, 'WORK31.liquid') SNIP = os.path.join(ROOT, 'snippets') T = open(TEMPLATE, encoding='utf-8').read() names = sorted(set(re.findall(r"(?:render|include) '(rfx-[a-z-]+)'", T))) S = {} for n in names: p = os.path.join(SNIP, n + '.liquid') assert os.path.exists(p), f'snippet missing on disk: {n}' S[n] = open(p, encoding='utf-8').read() for n, c in list(S.items()): # snippets rendered by snippets for m in set(re.findall(r"render '(rfx-[a-z-]+)'", c)): if m not in S: S[m] = open(os.path.join(SNIP, m + '.liquid'), encoding='utf-8').read() ALL = {'template': T, **S} checks, fails = 0, [] def check(cond, msg): global checks checks += 1 if not cond: fails.append(msg) # 1 size — Shopify caps theme files at 256 KB; 250 KB is the safety line for n, c in ALL.items(): check(len(c.encode('utf-8')) < 250_000, f'{n}: {len(c.encode())} bytes over the 250 KB line') # 2 Liquid tag balance for n, c in ALL.items(): for a, b in [('if','endif'),('for','endfor'),('case','endcase'),('capture','endcapture'),('comment','endcomment'),('unless','endunless'),('raw','endraw')]: oa = len(re.findall(r'\{%-?\s*' + a + r'\b', c)); ob = len(re.findall(r'\{%-?\s*' + b + r'\b', c)) check(oa == ob, f'{n}: {a}={oa} vs {b}={ob}') # 3 HTML container balance (comments stripped first so a tag name in prose cannot trip it) for n, c in ALL.items(): body = re.sub(r'\{%-?\s*comment\s*-?%\}.*?\{%-?\s*endcomment\s*-?%\}', '', c, flags=re.S) for tag in ['section','article','details','table','ul','ol','svg','figure','figcaption']: o = len(re.findall(r'<' + tag + r'[\s>]', body)); cl = len(re.findall(r'' + tag + r'>', body)) check(o == cl, f'{n}: <{tag}> open={o} close={cl}') # 4 at most one schema block, balanced check(len(re.findall(r'\{%-?\s*schema', T)) == len(re.findall(r'\{%-?\s*endschema', T)) <= 1, 'schema/endschema') # 5 ledger integrity: 9 fields per row, unique ids led = S.get('rfx-ledger-data', T) b = re.search(r'\{%- capture rfx_ledger -%\}\n(.*?)\n\s*\{%- endcapture -%\}', led, re.S) check(b is not None, 'ledger capture not found') rows = [r.strip() for r in b.group(1).split('~~~') if r.strip()] if b else [] ids = [r.split('|')[0] for r in rows] check(len(ids) == len(set(ids)), 'duplicate ledger ids: ' + str(sorted({i for i in ids if ids.count(i) > 1}))) for r in rows: check(r.count('|') == 8, 'ledger row field count: ' + r[:40]) # 6 every literal data-ev resolves to a ledger row refs = set(re.findall(r'data-ev="([a-z0-9-]+)"', ''.join(ALL.values()))) check(not (refs - set(ids)), 'data-ev without a ledger row: ' + str(sorted(refs - set(ids)))) # 7 every v_* variable used is assigned exactly once from the ledger loop used = set(re.findall(r'(v_[a-z0-9_]+)', T)); assigned = set(re.findall(r'assign (v_[a-z0-9_]+) =', T)) check(not (used - assigned), 'v_ used but never assigned: ' + str(sorted(used - assigned))) # 8 no superseded figure hardcoded in body copy outside a prior-value context body = T[T.index('
', '54,886 lines'] for k in stale: for m in re.finditer(re.escape(k), body): ctx = body[max(0, m.start() - 140):m.start()] check(any(x in ctx for x in [']*>(.*?)', code, re.S): check('{{' not in pre and '{%' not in pre, 'Liquid syntax inside a code specimen outside raw') # 15 timeline rows well-formed tl = re.search(r'capture tl_data -%\}(.*?)\{%- endcapture', T, re.S) if tl: for r in [x for x in tl.group(1).split('~') if x.strip()]: check(r.strip().count('|') == 3, 'timeline row: ' + r.strip()[:40]) result = {'assertions': checks, 'failures': len(fails), 'date': datetime.date.today().isoformat(), 'files': list(ALL.keys())} print(json.dumps(result)) for f in fails: print(' - ' + f, file=sys.stderr) sys.exit(1 if fails else 0)