Every figure in the Generalized Consensus series that was authored as an animation — 15 stepped manually via a Next button, 1 autoplayed with a replay button — now renders as a single static image showing all animation states superimposed at once. The prose still refers to "the animation above" / "the following animated example", so the affected sections are not just un-animated, they're unreadable — several figures on a page are byte-identical images.
Affected pages
16 animations across 5 posts, each duplicated in the blog and docs copies (10 routes total):
| Route |
Animations |
/blog/generalized-consensus-part4 · /docs/consensus/part-04 |
1 |
/blog/generalized-consensus-part6 · /docs/consensus/part-06 |
5 |
/blog/generalized-consensus-part7 · /docs/consensus/part-07 |
6 |
/blog/generalized-consensus-part8 · /docs/consensus/part-08 |
3 |
/blog/generalized-consensus-part10 · /docs/consensus/part-10 |
1 |
Clearest example: https://multigres.com/blog/generalized-consensus-part7. Figures 3, 4, 5, 6, 7 and 8 are six <img> tags pointing at the same part07-fig3.svg:
<img alt="Figure 3: Raft timeline propagation" src="/assets/part07-fig3-DEk2p0Pr.svg" …>
<img alt="Figure 4: Initial state" src="/assets/part07-fig3-DEk2p0Pr.svg" …>
<img alt="Figure 5: Scenario 2" src="/assets/part07-fig3-DEk2p0Pr.svg" …>
<img alt="Figure 6: Scenario 3" src="/assets/part07-fig3-DEk2p0Pr.svg" …>
<img alt="Figure 7: Scenario 4" src="/assets/part07-fig3-DEk2p0Pr.svg" …>
<img alt="Figure 8: Scenario 5" src="/assets/part07-fig3-DEk2p0Pr.svg" …>
Why the images look wrong, not merely static
These SVGs are not pictures of one state — they are the whole sequence stacked in one file, with every state given an id. part07-fig3.svg carries 70 ids: per-node term/value slots (n1t3…n6v5), nine message arrows (n1n6, n3n5, n4n1, n4n2, n4n3, n4n5, n4n6, n5n3, n5n4) and five step captions (desc1–desc5).
A GSAP timeline fetched the SVG, inlined it, hid everything not yet reached, and revealed one step per click. Reusing one file across six figures was deliberate: same artwork, different timeline. With the timeline gone, the browser paints all 70 elements simultaneously — every term, every arrow and all five step captions on top of each other.
Root cause
Commit ac5d0a5 — "Migrate to Fumadocs (#56)" (2026-06-02, 227 files changed, 11,411 insertions, 42,459 deletions). The animation layer was 17 of the 48 files that commit deleted:
src/components/AnimatedSVG.tsx — React wrapper (fetch + inline + Next/Reset controls)
src/lib/svg-animator.ts — the SVGAnimator GSAP engine
src/lib/svg-animator.README.md
- 14 per-figure timeline scripts:
part06Fig1, part06Fig2Scenario1-3, part06Fig3, part07Fig3Raft, part07Fig3Scenario2-5, part08Fig3a/b/c, requestProcessing
gsap@^3.13.0 was also dropped from package.json
The lossy conversion happened in scripts/migrate-from-docusaurus.mjs:93-100:
out = out.replace(
/<AnimatedSVG[\s\S]*?src=\{useBaseUrl\(['"]([^'"]+)['"]\)\}[\s\S]*?alt="([^"]*)"[\s\S]*?\/>/g,
(_, src, alt) => `\n\n`,
);
The regex captures only src and alt. onAnimate={part07Fig3Raft} — the entire animation — is absorbed by the [\s\S]*? and discarded. Line 88 (out.replace(/^import\s+.+$/gm, '')) strips the accompanying imports. So
<AnimatedSVG src={useBaseUrl('/img/consensus/part07-fig3.svg')} onAnimate={part07Fig3Scenario5} … />
became

Verification
- The animations were correctly wired immediately before the migration: at
ac5d0a5^, all 16 onAnimate={…} references in content (14 distinct animations — part07Fig3Raft and part06Fig3 are each used twice) resolve to an exported animation script, with zero dangling references.
- Only one commit ever broke this. Full history of the animation infrastructure:
c8d144e (added, 2025-10-21), 9a8e8ba, 183475e (part 4 added), 4422d47 (ToC overlap fix, 2025-11-03), then ac5d0a5 (deleted). Nothing after ac5d0a5 touches it.
animated-svg-container appears 0 times in the current production HTML.
- Internet Archive brackets the regression: the 2026-05-18 capture has 6 animation containers and 0 static figure images; the 2026-07-08 capture has 0 containers and 6 static images.
Not lost
Every SVG asset survived the migration intact — they were moved from static/img/consensus/ to public/img/consensus/. That's why the pages render something instead of broken images, and it means no artwork needs to be recreated. Everything deleted is recoverable verbatim from ac5d0a5^.
Note that the 16 animations are driven by only 6 distinct SVG files — part06-fig1, part06-fig2, part06-fig3, part07-fig3, part08-fig3 and requestProcessing. Reuse was deliberate: one artwork, several timelines over it. That is exactly why six figures on part 7 now render as the same image.
Also worth knowing: the animated SVGs were only ever requested via a runtime fetch(), so the Wayback crawler never discovered their URLs. multigres.com/img/consensus/part07-fig3.svg has zero captures (part07-fig1/2/6, which were ordinary markdown images, were captured). There is therefore no archived copy of the working animations to compare against — the only faithful reference is a local build of ac5d0a5^.
Minor issues in the same area
Not part of the regression, but visible on part 7:
- The final figure is labelled
Figure 6: Timeline Priority (content/blog/generalized-consensus-part7.md:273) though it is the 9th figure.
- Parts 6, 7 and 10 still contain orphaned
<div style={{textAlign: 'center', …}}> wrappers left over from the removed <AnimatedSVG> blocks (e.g. content/blog/generalized-consensus-part7.md:248-252). In a .md file these are inert raw HTML — the style={{…}} attribute is not valid HTML and does nothing.
Suggested direction
A full revert of ac5d0a5 is not viable — it would restore the entire Docusaurus site. A targeted restore works instead: bring back the 17 deleted animation files and the gsap dependency, then reconnect them. The original wiring cannot be reverted as-is because it depended on @site/* (no longer in tsconfig.json) and @docusaurus/useBaseUrl (Docusaurus is fully removed), and because the migration rewrote the content files into plain markdown images.
The reconnection can be done entirely in the renderer with no content changes: the migration discarded onAnimate but preserved alt, and all 16 (alt, svg) pairs are unique, so the caption is sufficient to recover which timeline belongs to which figure. Overriding img in src/components/mdx.tsx to swap those 16 figures for AnimatedSVG fixes all 5 posts and both the blog and docs copies at once.
Every figure in the Generalized Consensus series that was authored as an animation — 15 stepped manually via a Next button, 1 autoplayed with a replay button — now renders as a single static image showing all animation states superimposed at once. The prose still refers to "the animation above" / "the following animated example", so the affected sections are not just un-animated, they're unreadable — several figures on a page are byte-identical images.
Affected pages
16 animations across 5 posts, each duplicated in the blog and docs copies (10 routes total):
/blog/generalized-consensus-part4·/docs/consensus/part-04/blog/generalized-consensus-part6·/docs/consensus/part-06/blog/generalized-consensus-part7·/docs/consensus/part-07/blog/generalized-consensus-part8·/docs/consensus/part-08/blog/generalized-consensus-part10·/docs/consensus/part-10Clearest example: https://multigres.com/blog/generalized-consensus-part7. Figures 3, 4, 5, 6, 7 and 8 are six
<img>tags pointing at the samepart07-fig3.svg:Why the images look wrong, not merely static
These SVGs are not pictures of one state — they are the whole sequence stacked in one file, with every state given an id.
part07-fig3.svgcarries 70 ids: per-node term/value slots (n1t3…n6v5), nine message arrows (n1n6,n3n5,n4n1,n4n2,n4n3,n4n5,n4n6,n5n3,n5n4) and five step captions (desc1–desc5).A GSAP timeline fetched the SVG, inlined it, hid everything not yet reached, and revealed one step per click. Reusing one file across six figures was deliberate: same artwork, different timeline. With the timeline gone, the browser paints all 70 elements simultaneously — every term, every arrow and all five step captions on top of each other.
Root cause
Commit
ac5d0a5— "Migrate to Fumadocs (#56)" (2026-06-02, 227 files changed, 11,411 insertions, 42,459 deletions). The animation layer was 17 of the 48 files that commit deleted:src/components/AnimatedSVG.tsx— React wrapper (fetch + inline + Next/Reset controls)src/lib/svg-animator.ts— theSVGAnimatorGSAP enginesrc/lib/svg-animator.README.mdpart06Fig1,part06Fig2Scenario1-3,part06Fig3,part07Fig3Raft,part07Fig3Scenario2-5,part08Fig3a/b/c,requestProcessinggsap@^3.13.0was also dropped frompackage.jsonThe lossy conversion happened in
scripts/migrate-from-docusaurus.mjs:93-100:The regex captures only
srcandalt.onAnimate={part07Fig3Raft}— the entire animation — is absorbed by the[\s\S]*?and discarded. Line 88 (out.replace(/^import\s+.+$/gm, '')) strips the accompanying imports. Sobecame
Verification
ac5d0a5^, all 16onAnimate={…}references in content (14 distinct animations —part07Fig3Raftandpart06Fig3are each used twice) resolve to an exported animation script, with zero dangling references.c8d144e(added, 2025-10-21),9a8e8ba,183475e(part 4 added),4422d47(ToC overlap fix, 2025-11-03), thenac5d0a5(deleted). Nothing afterac5d0a5touches it.animated-svg-containerappears 0 times in the current production HTML.Not lost
Every SVG asset survived the migration intact — they were moved from
static/img/consensus/topublic/img/consensus/. That's why the pages render something instead of broken images, and it means no artwork needs to be recreated. Everything deleted is recoverable verbatim fromac5d0a5^.Note that the 16 animations are driven by only 6 distinct SVG files —
part06-fig1,part06-fig2,part06-fig3,part07-fig3,part08-fig3andrequestProcessing. Reuse was deliberate: one artwork, several timelines over it. That is exactly why six figures on part 7 now render as the same image.Also worth knowing: the animated SVGs were only ever requested via a runtime
fetch(), so the Wayback crawler never discovered their URLs.multigres.com/img/consensus/part07-fig3.svghas zero captures (part07-fig1/2/6, which were ordinary markdown images, were captured). There is therefore no archived copy of the working animations to compare against — the only faithful reference is a local build ofac5d0a5^.Minor issues in the same area
Not part of the regression, but visible on part 7:
Figure 6: Timeline Priority(content/blog/generalized-consensus-part7.md:273) though it is the 9th figure.<div style={{textAlign: 'center', …}}>wrappers left over from the removed<AnimatedSVG>blocks (e.g.content/blog/generalized-consensus-part7.md:248-252). In a.mdfile these are inert raw HTML — thestyle={{…}}attribute is not valid HTML and does nothing.Suggested direction
A full revert of
ac5d0a5is not viable — it would restore the entire Docusaurus site. A targeted restore works instead: bring back the 17 deleted animation files and thegsapdependency, then reconnect them. The original wiring cannot be reverted as-is because it depended on@site/*(no longer intsconfig.json) and@docusaurus/useBaseUrl(Docusaurus is fully removed), and because the migration rewrote the content files into plain markdown images.The reconnection can be done entirely in the renderer with no content changes: the migration discarded
onAnimatebut preservedalt, and all 16(alt, svg)pairs are unique, so the caption is sufficient to recover which timeline belongs to which figure. Overridingimginsrc/components/mdx.tsxto swap those 16 figures forAnimatedSVGfixes all 5 posts and both the blog and docs copies at once.