Skip to content

Instantly share code, notes, and snippets.

@takahirom
Created August 7, 2026 10:18
Show Gist options
  • Select an option

  • Save takahirom/3720db63e03ee3cbb0d0cc921be3fddf to your computer and use it in GitHub Desktop.

Select an option

Save takahirom/3720db63e03ee3cbb0d0cc921be3fddf to your computer and use it in GitHub Desktop.
commit-graph-figures-SKILL.md
name commit-graph-figures
description Draw commit-graph comparison figures (hand-written SVG in HTML) for articles, PR descriptions, and docs. Use when the user wants a git graph diagram showing which two commits/refs get compared (baselines, merge refs, fork points), wants multi-panel figures of the same graph with different comparisons highlighted, or asks to visualize CI/merge-ref behavior.

Commit-graph comparison figures

One topology, many panels. The graph (commits, branches, synthetic merge commits) is defined once as data; each panel re-renders that identical topology and varies only its spotlight — which pair is compared, what is dimmed — and its verdict. Readers keep a single mental model across panels; hand-drawing any panel separately breaks that and is the primary failure to avoid.

Steps

  1. Define the topology as data. Nodes ({x, y, fill, label}) and edges ([from, to] pairs) in one JS object/array inside the HTML. Give nodes short stable names (M1..Mn for main, A1 base, B1 head, T1/T2 GitHub test commits, S self-made merge) and reuse those names verbatim in the surrounding prose. Done when: every panel can render from this single source and moving one node moves it in all panels.

  2. Define panels as configs, not drawings. Each panel is {title, pair: [a, b], dim: [...], verdict, caption}. pair gets double rings and the comparison arc; dim renders at ~25% opacity; nodes irrelevant to the whole figure set are omitted, not dimmed. Done when: adding a panel is a config entry, zero new drawing code.

  3. Render with a small fixed vocabulary of primitives.

    • Commit: <circle r="15">; synthetic commits (test merges, CI-built merges) get a dashed stroke and pale fill — they are "not on any branch" and must look it.
    • Edge: <line>, one neutral gray.
    • Comparison arc: quadratic <path> with <marker> arrowheads on both ends, dashed, routed through empty canvas space.
    • Text halo: paint-order: stroke with a thick background-colored stroke, so labels stay legible over edges.
    • Branch lanes: faint horizontal guide lines with italic lane labels at the left edge.
  4. Fix the verdict position across panels. One line, bottom-center, same coordinates in every panel: what the diff contains ("diff = B1 only — ..."). Floating verdicts near their arcs collide with lane labels and read differently per panel.

  5. Ship as screenshot-ready cards. Each panel sits on a fixed white card (background:#fff, border, radius) regardless of page theme, so a screenshot looks identical wherever it lands — most publishing targets render neither mermaid nor raw SVG. Colors must pass a CVD (color-blindness) check; do not eyeball palettes — a validated safe set: orange #f08c00 (head), blue #1971c2 (base), red #e03131 (drift/noise), violet #9c36b5 (synthetic), gray #868e96 (shared history).

  6. Render it and look at it. Coordinates and arc control points are hand-tuned guesses until a human (or screenshot) confirms them. Done when: at full size, no text overlaps any other text or node — a halo partially hiding another label counts as overlap, not as acceptable layering.

Skeleton

<script>
const NODES = { M1:{x:160,y:470,fill:"#868e96",label:"M1"}, /* ... */ };
const EDGES = [["M1","M2"],["M1","A1"],["A1","B1"] /* ... */];
const PANELS = [
  { title:"Today ❌", pair:["A1","T2"], dim:["S"],
    verdict:"diff = B1 + M2·M3 — drift leaks in", caption:"..." },
  // one entry per comparison approach
];
// render(): map EDGES→<line>, NODES→<circle>+label, pair→rings+arc, verdict→fixed bottom text
document.getElementById("figs").innerHTML = PANELS.map(render).join("");
</script>

Pitfalls

  • Verdict/label collisions hide behind the halo: the white stroke makes an overlapped label look like corrupted ghost text rather than an obvious crash. This is why step 6 is a real step.
  • Prose drift: if the article says "the base branch tip" while the figure says A1, the figure stops paying rent. Introduce the node names once in prose, then use them everywhere.
  • Per-panel graphs: the temptation is to simplify each panel's graph to "just what it needs". Resist — the shared topology is the point; use dim instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment