site SVG: geometry that survives growth — adaptive leaf boxes, self-sizing head box

Operator-caught, by eye: the accumulator graphic was designed for 8
leaves (fixed 112px boxes, fixed 380px head box) and has been a
shingled overlap since the log reached 19 on 2026-08-08; the head
caption spilled both sides after today's wording grew. Every 'site
verified' since was text-probes — nobody rendered pixels. Fixes:
leaf boxes now size to their span (rich >=100px, compact below, short
names, hash line dropped when narrow); the head box computes its width
from its longest line and carries the caption on two lines; edges
anchor to the actual box top. New regression test renders the tree at
8/19/33 leaves and asserts no two leaf rects overlap and the head
title fits its box. Suite 154 green. Eye-passed locally before deploy.
This commit is contained in:
mrwulf 2026-08-16 20:22:45 +02:00
parent 2517d79f6e
commit 51a094182a
2 changed files with 63 additions and 9 deletions

View file

@ -50,6 +50,13 @@ def _leaf_ok(entry: LogEntry) -> bool:
) )
def _leaf_short(component: str) -> str:
"""Compact display name for a leaf box at small spans."""
return (component.replace("-ed25519-verified", "")
.replace("ltl-accumulator-verified", "accum")
.replace("fips205-slhdsa-verified", "slh-dsa"))
def _svg_tree(entries: list[LogEntry], root_hex: str, signing_backend: str, head_label: str = "Ed25519") -> str: def _svg_tree(entries: list[LogEntry], root_hex: str, signing_backend: str, head_label: str = "Ed25519") -> str:
"""The accumulator, drawn from its real leaves.""" """The accumulator, drawn from its real leaves."""
if not entries: if not entries:
@ -77,12 +84,22 @@ def _svg_tree(entries: list[LogEntry], root_hex: str, signing_backend: str, head
ok = _leaf_ok(entry) ok = _leaf_ok(entry)
component = (((entry.leaf.get("attestation") or {}).get("subject")) or {}).get("component", "?") component = (((entry.leaf.get("attestation") or {}).get("subject")) or {}).get("component", "?")
fill, stroke = ("#e2f2e9", "#1e7f4f") if ok else ("#f4f4f6", "#8a93a0") fill, stroke = ("#e2f2e9", "#1e7f4f") if ok else ("#f4f4f6", "#8a93a0")
out.append(f'<rect x="{x-56}" y="{y-22}" width="112" height="44" rx="5" fill="{fill}" stroke="{stroke}" stroke-width="1.4"/>') # Boxes must FIT the per-leaf span at any tree size (the
out.append(f'<text x="{x}" y="{y-6}" text-anchor="middle" fill="#333">leaf {node_index}</text>') # 2026-08-16 lesson: fixed 112px boxes shingled at 19
short = escape(str(component).replace("-ed25519-verified", "")) # leaves). Rich boxes while they fit, compact ones after.
box_w = min(112.0, span * 0.94)
compact = box_w < 100
short = escape(_leaf_short(str(component)))
label = short if ok else f"{short}" label = short if ok else f"{short}"
out.append(f'<text x="{x}" y="{y+8}" text-anchor="middle" fill="{stroke}">{label}</text>') if compact:
out.append(f'<text x="{x}" y="{y+19}" text-anchor="middle" fill="#999" font-size="9">{node.hex()[:10]}…</text>') out.append(f'<rect x="{x-box_w/2:.1f}" y="{y-18}" width="{box_w:.1f}" height="36" rx="4" fill="{fill}" stroke="{stroke}" stroke-width="1.2"/>')
out.append(f'<text x="{x}" y="{y-4}" text-anchor="middle" fill="#333" font-size="8">leaf {node_index}</text>')
out.append(f'<text x="{x}" y="{y+9}" text-anchor="middle" fill="{stroke}" font-size="7">{label}</text>')
else:
out.append(f'<rect x="{x-box_w/2:.1f}" y="{y-22}" width="{box_w:.1f}" height="44" rx="5" fill="{fill}" stroke="{stroke}" stroke-width="1.4"/>')
out.append(f'<text x="{x}" y="{y-6}" text-anchor="middle" fill="#333">leaf {node_index}</text>')
out.append(f'<text x="{x}" y="{y+8}" text-anchor="middle" fill="{stroke}">{label}</text>')
out.append(f'<text x="{x}" y="{y+19}" text-anchor="middle" fill="#999" font-size="9">{node.hex()[:10]}…</text>')
else: else:
is_root = level_index == len(levels) - 1 is_root = level_index == len(levels) - 1
out.append(f'<rect x="{x-50}" y="{y-15}" width="100" height="30" rx="5" fill="{"#eef0f7" if is_root else "#fff"}" stroke="{"#3b4d8f" if is_root else "#bbb"}" stroke-width="{1.6 if is_root else 1}"/>') out.append(f'<rect x="{x-50}" y="{y-15}" width="100" height="30" rx="5" fill="{"#eef0f7" if is_root else "#fff"}" stroke="{"#3b4d8f" if is_root else "#bbb"}" stroke-width="{1.6 if is_root else 1}"/>')
@ -91,11 +108,19 @@ def _svg_tree(entries: list[LogEntry], root_hex: str, signing_backend: str, head
for child in (2 * node_index, 2 * node_index + 1): for child in (2 * node_index, 2 * node_index + 1):
if (level_index - 1, child) in positions: if (level_index - 1, child) in positions:
cx, cy = positions[(level_index - 1, child)] cx, cy = positions[(level_index - 1, child)]
out.append(f'<line x1="{x}" y1="{y+15}" x2="{cx}" y2="{cy-22 if level_index==1 else cy-15}" stroke="#ccc"/>') leaf_top = 18 if len(entries) > 9 else 22
out.append(f'<line x1="{x}" y1="{y+15}" x2="{cx}" y2="{cy-leaf_top if level_index==1 else cy-15}" stroke="#ccc"/>')
root_x, root_y = positions[(len(levels) - 1, 0)] root_x, root_y = positions[(len(levels) - 1, 0)]
out.append(f'<rect x="{root_x-190}" y="{root_y-72}" width="380" height="34" rx="6" fill="#e2f2e9" stroke="#1e7f4f" stroke-width="1.6"/>') # The head box sizes itself to its longest line (the 2026-08-16
out.append(f'<text x="{root_x}" y="{root_y-58}" text-anchor="middle" fill="#1e7f4f" font-weight="bold">Signed Tree Head — {escape(head_label)}({root_hex[:12]}…)</text>') # lesson: a fixed 380px box let a growing caption spill both sides).
out.append(f'<text x="{root_x}" y="{root_y-46}" text-anchor="middle" fill="#1e7f4f" font-size="9">signed by: {escape(signing_backend)} (verify path attested; signing itself not proven)</text>') title = f"Signed Tree Head — {head_label}({root_hex[:12]}…)"
line2 = f"signed by: {signing_backend}"
line3 = "(verify path attested; signing itself not proven)"
head_w = max(len(title) * 7.0, len(line2) * 5.3, len(line3) * 5.3) + 28
out.append(f'<rect x="{root_x-head_w/2:.1f}" y="{root_y-84}" width="{head_w:.1f}" height="46" rx="6" fill="#e2f2e9" stroke="#1e7f4f" stroke-width="1.6"/>')
out.append(f'<text x="{root_x}" y="{root_y-70}" text-anchor="middle" fill="#1e7f4f" font-weight="bold">{escape(title)}</text>')
out.append(f'<text x="{root_x}" y="{root_y-58}" text-anchor="middle" fill="#1e7f4f" font-size="9">{escape(line2)}</text>')
out.append(f'<text x="{root_x}" y="{root_y-47}" text-anchor="middle" fill="#1e7f4f" font-size="9">{escape(line3)}</text>')
out.append(f'<line x1="{root_x}" y1="{root_y-38}" x2="{root_x}" y2="{root_y-15}" stroke="#1e7f4f" stroke-width="1.4"/>') out.append(f'<line x1="{root_x}" y1="{root_y-38}" x2="{root_x}" y2="{root_y-15}" stroke="#1e7f4f" stroke-width="1.4"/>')
out.append("</svg>") out.append("</svg>")
return "".join(out) return "".join(out)

View file

@ -181,3 +181,32 @@ def test_webdocs_source_carries_no_stale_paper_claims():
# first-use glosses the page promised: STH and axiom cones # first-use glosses the page promised: STH and axiom cones
assert "Signed Tree Head (STH)" in text assert "Signed Tree Head (STH)" in text
assert "axiom cones (the exact set of assumptions" in text assert "axiom cones (the exact set of assumptions" in text
def test_svg_tree_boxes_never_overlap_or_spill():
# Regression for 2026-08-16: fixed-width leaf boxes shingled once the
# log outgrew the 8-leaf design, and a fixed head box let its caption
# spill. Render the tree at several sizes and assert geometry.
import re
from pacta_provider.webdocs import _svg_tree
class _E:
def __init__(self, i):
self.leaf_hash = f"{i:02x}" * 32
self.leaf = {"attestation": {"subject": {"component": "betrusted-ed25519-verified"},
"certificates": [{"status": "proven", "axiom_status": "clean"}]}}
for n in (8, 19, 33):
svg = _svg_tree([_E(i) for i in range(n)], "ab" * 32, "verified-dalek-serial")
rects = [(float(m.group(1)), float(m.group(2)), float(m.group(3)))
for m in re.finditer(r'<rect x="([-0-9.]+)" y="([0-9.]+)" width="([0-9.]+)"', svg)]
leaf_y = max(y for _, y, _ in rects)
leaves = sorted((x, w) for x, y, w in rects if y == leaf_y)
assert len(leaves) == n
for (x1, w1), (x2, _w2) in zip(leaves, leaves[1:]):
assert x1 + w1 <= x2 + 0.01, f"leaf boxes overlap at n={n}"
# head caption must fit its box: longest line estimated at 5.3px/char
head = re.search(r'<rect x="[-0-9.]+" y="[0-9.]+" width="([0-9.]+)" height="46"', svg)
title = re.search(r'font-weight="bold">([^<]+)</text>', svg).group(1)
assert len(title) * 7.0 <= float(head.group(1)), "head title spills"