Files
diagram-design/skills/diagram-design/assets/example-ridgeline-dark.html
T
ddaeae936e feat(line): add ridgeline as a Line variant (#143)
* feat(line): add ridgeline as a Line variant

One distribution per series, stacked at a fixed pitch on a single shared
amplitude. Ships three static examples (light, dark, full editorial),
the type-line.md spec, and a geometric gate.

verify-ridgeline.py derives the figure's one amplitude and its baseline
pitch from the figure itself, then holds every vertex to both. It also
checks the drawn baseline rules, the shared bin scale, absolute M/L/Z
only, the overlap ceiling, the focus pairing and every label binding.
test-verify-ridgeline.py proves each check in both polarities and pins
the scope treaty with the sibling Line gates.

The contract binds data-bins on a <path>, which no other chart in the
repo declares, so verify-slopegraph and this gate never claim the same
file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(verify-ridgeline): read exponent coordinates as numbers, not commands

parse_d scanned for [A-Za-z] independently of the numbers, so the `e` in a
valid scientific-notation coordinate such as 4.4e2 was counted as a path
command. The explicit-command rule then rejected a correct ridgeline with a
message about command counts.

Tokenise the d attribute left to right with the number branch tried first,
so an exponent is consumed as part of its number. A letter that survives
that branch is a real command and still faces the M/L/Z vocabulary check;
anything else leaves the path unreadable, which stays a finding.

Adds both polarities: a shipped coordinate rewritten as 4.4e2 must verify,
and a falsified one written the same way must not.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: 0xDarkMatter <0xDarkMatter@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 12:23:44 -05:00

105 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Request latency by service · Ridgeline</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #2d3142;
--color-ink: #f5f5f5;
--color-muted: #bfc0c0;
--color-accent: #f08a59;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Ridgeline · Diagram Design</p>
<h1>Checkout is slow in two different ways</h1>
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="ridgeline-dark-title ridgeline-dark-desc">
<title id="ridgeline-dark-title">Checkout is slow in two different ways</title>
<desc id="ridgeline-dark-desc">Ridgeline chart of request-latency distributions for five services over one week; each ridge is the share of that service's requests falling in 40-millisecond bins, drawn on one shared amplitude scale, and checkout-api carries a second peak past 300 milliseconds that none of the other four show.</desc>
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#2d3142"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- ONE amplitude on every ridge: y = baseline - 2.4 x (share in %). Per-ridge
normalisation would let a rare, flat distribution wear the same silhouette
as a tight one, which is the single lie this type is built to tell.
scripts/verify-ridgeline.py derives the amplitude from the figure and then
holds every vertex of every ridge to it. -->
<text transform="rotate(-90 24 264)" x="24" y="264" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SHARE OF REQUESTS · 1% = 2.4PX ON EVERY RIDGE</text>
<!-- One baseline per ridge, 56px pitch. The baseline is where a bin of zero
requests sits, so it can never be moved to give a ridge more room. -->
<line data-ridge="static-cdn" data-role="baseline" x1="320" y1="152" x2="680" y2="152" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
<line data-ridge="auth-api" data-role="baseline" x1="320" y1="208" x2="680" y2="208" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
<line data-ridge="search-api" data-role="baseline" x1="320" y1="264" x2="680" y2="264" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
<line data-ridge="checkout-api" data-role="baseline" x1="320" y1="320" x2="680" y2="320" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
<line data-ridge="report-export" data-role="baseline" x1="320" y1="376" x2="680" y2="376" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
<!-- Shared x-scale: every ridge is sampled at the same 13 bins, 40ms wide,
labelled at the lower edge. Straight segments between bins - no smoothing,
because a spline through 13 bins invents extrema the sample never had. -->
<path data-ridge="static-cdn" data-baseline="152" data-bins="0,26,34,21,11,5,2,1,0,0,0,0,0" d="M320,152 L350,89.6 L380,70.4 L410,101.6 L440,125.6 L470,140 L500,147.2 L530,149.6 L560,152 L590,152 L620,152 L650,152 L680,152 Z" fill="rgba(245,245,245,0.12)" stroke="rgba(245,245,245,0.80)" stroke-width="1.2" stroke-linejoin="round"/>
<text data-ridge="static-cdn" data-role="name" x="304" y="155.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif" text-anchor="end">static-cdn</text>
<text data-ridge="static-cdn" data-role="range" x="696" y="155.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">40–280 ms</text>
<path data-ridge="auth-api" data-baseline="208" data-bins="0,6,28,36,18,7,3,1,1,0,0,0,0" d="M320,208 L350,193.6 L380,140.8 L410,121.6 L440,164.8 L470,191.2 L500,200.8 L530,205.6 L560,205.6 L590,208 L620,208 L650,208 L680,208 Z" fill="rgba(245,245,245,0.12)" stroke="rgba(245,245,245,0.74)" stroke-width="1.2" stroke-linejoin="round"/>
<text data-ridge="auth-api" data-role="name" x="304" y="211.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif" text-anchor="end">auth-api</text>
<text data-ridge="auth-api" data-role="range" x="696" y="211.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">40–320 ms</text>
<path data-ridge="search-api" data-baseline="264" data-bins="0,1,9,22,27,19,11,6,3,1,1,0,0" d="M320,264 L350,261.6 L380,242.4 L410,211.2 L440,199.2 L470,218.4 L500,237.6 L530,249.6 L560,256.8 L590,261.6 L620,261.6 L650,264 L680,264 Z" fill="rgba(245,245,245,0.12)" stroke="rgba(245,245,245,0.68)" stroke-width="1.2" stroke-linejoin="round"/>
<text data-ridge="search-api" data-role="name" x="304" y="267.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif" text-anchor="end">search-api</text>
<text data-ridge="search-api" data-role="range" x="696" y="267.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">40–400 ms</text>
<!-- checkout-api FOCAL - and it is the ridge whose SHAPE is the story,
not the slowest service. Drawn last so its second peak reads over the
ridge below it rather than under. -->
<path data-ridge="checkout-api" data-baseline="320" data-bins="0,1,6,17,21,14,8,6,7,9,7,4,0" d="M320,320 L350,317.6 L380,305.6 L410,279.2 L440,269.6 L470,286.4 L500,300.8 L530,305.6 L560,303.2 L590,298.4 L620,303.2 L650,310.4 L680,320 Z" fill="rgba(240,138,89,0.16)" stroke="#f08a59" stroke-width="2.4" stroke-linejoin="round"/>
<text data-ridge="checkout-api" data-role="name" x="304" y="323.5" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">checkout-api</text>
<text data-ridge="checkout-api" data-role="range" x="696" y="323.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">40–440 ms</text>
<path data-ridge="report-export" data-baseline="376" data-bins="0,0,1,3,7,12,17,19,16,12,9,4,0" d="M320,376 L350,376 L380,373.6 L410,368.8 L440,359.2 L470,347.2 L500,335.2 L530,330.4 L560,337.6 L590,347.2 L620,354.4 L650,366.4 L680,376 Z" fill="rgba(245,245,245,0.12)" stroke="rgba(245,245,245,0.62)" stroke-width="1.2" stroke-linejoin="round"/>
<text data-ridge="report-export" data-role="name" x="304" y="379.5" fill="#f5f5f5" font-size="11" font-weight="500" font-family="'Geist', sans-serif" text-anchor="end">report-export</text>
<text data-ridge="report-export" data-role="range" x="696" y="379.5" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace">80–440 ms</text>
<!-- Bin ticks on the shared latency scale. data-bin binds the printed number,
so a caption cannot be edited without its binding. -->
<text data-tick="0" data-bin="0" x="320" y="400" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">0</text>
<text data-tick="1" data-bin="120" x="410" y="400" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">120</text>
<text data-tick="2" data-bin="240" x="500" y="400" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">240</text>
<text data-tick="3" data-bin="360" x="590" y="400" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">360</text>
<text data-tick="4" data-bin="480" x="680" y="400" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">480</text>
<text x="500" y="424" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.18em" text-anchor="middle">REQUEST LATENCY (MS), BIN LOWER EDGE</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<text x="960" y="478" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="end">SHARE OF EACH SERVICE'S OWN REQUESTS PER 40 MS BIN · 1% = 2.4PX ON EVERY RIDGE · UNSMOOTHED · ILLUSTRATIVE FIGURES</text>
<rect x="40" y="486" width="24" height="10" fill="rgba(240,138,89,0.16)"/>
<line x1="40" y1="486" x2="64" y2="486" stroke="#f08a59" stroke-width="2.4"/>
<text x="72" y="496" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">checkout-api · focal, second peak past 300 ms</text>
<rect x="360" y="486" width="24" height="10" fill="rgba(245,245,245,0.12)"/>
<line x1="360" y1="486" x2="384" y2="486" stroke="rgba(245,245,245,0.80)" stroke-width="1.2"/>
<text x="392" y="496" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Other services · strongest tone is the fastest median</text>
</svg>
</div>
</body>
</html>