Manim Quality Testkit Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Testkit that validates manim diagram geometry (overlaps, crossings, clipping, near-parallel lines) at the scene-graph level, without rendering.
Architecture: Extractor builds ParsedScene from manim mobjects → 4 check functions → pytest harness with must-fail fixtures → Dagger CI + pre-commit integration.
Tech Stack: manim, shapely, pytest
Task 1: Data model and scene extractor
Files:
- Create:
docs/diagrams/manim_quality.py
Step 1: Write the failing test
Create docs/diagrams/test_manim_quality.py with a single test that imports and calls the extractor:
# /// script
# requires-python = ">=3.10"
# dependencies = ["manim", "shapely", "pytest"]
# ///
import numpy as np
from manim import Arrow, Scene, Text
from manim_quality import extract_scene, ParsedScene
class SimpleTestScene(Scene):
def construct(self):
self.add(Text("A", font_size=28).move_to(np.array([1.0, 2.0, 0.0])))
self.add(Arrow(np.array([0, 0, 0.0]), np.array([2, 1, 0.0]), buff=0))
def test_extract_scene_returns_parsed_scene():
result = extract_scene(SimpleTestScene)
assert isinstance(result, ParsedScene)
assert result.scene_name == "SimpleTestScene"
assert len(result.labels) == 1
assert result.labels[0].name == "A"
assert abs(result.labels[0].center.x - 1.0) < 0.01
assert len(result.arrows) == 1
assert abs(result.arrows[0].start.x) < 0.01
assert abs(result.arrows[0].end.x - 2.0) < 0.01
assert result.frame_width > 14
assert result.frame_height > 7Step 2: Run test to verify it fails
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::test_extract_scene_returns_parsed_scene -v
Expected: FAIL with ModuleNotFoundError: No module named 'manim_quality'
Step 3: Write minimal implementation
Create docs/diagrams/manim_quality.py:
# /// script
# requires-python = ">=3.10"
# dependencies = ["manim", "shapely"]
# ///
"""Manim diagram quality checks — scene-graph level validation.
Extracts exact geometry from manim mobjects after construct() and runs
checks for overlapping labels, arrows crossing text, elements outside
viewport, and near-parallel lines.
"""
from __future__ import annotations
import math
from dataclasses import dataclass
from manim import (
Arrow,
DashedLine,
Line,
MathTex,
Polygon,
Scene,
Text,
config,
)
from manim.constants import DOWN, LEFT, RIGHT, UP
from shapely.geometry import LineString, box as shapely_box
@dataclass
class Point:
x: float
y: float
@dataclass
class BBox:
x_min: float
y_min: float
x_max: float
y_max: float
def overlaps(self, other: BBox, *, padding: float = 0.0) -> bool:
return (
self.x_min - padding < other.x_max + padding
and self.x_max + padding > other.x_min - padding
and self.y_min - padding < other.y_max + padding
and self.y_max + padding > other.y_min - padding
)
def to_shapely(self, *, padding: float = 0.0):
return shapely_box(
self.x_min - padding,
self.y_min - padding,
self.x_max + padding,
self.y_max + padding,
)
@dataclass
class Label:
name: str
bbox: BBox
center: Point
@dataclass
class ArrowSegment:
name: str
start: Point
end: Point
@dataclass
class LineSegment:
name: str
start: Point
end: Point
@dataclass
class ParsedScene:
scene_name: str
labels: list[Label]
arrows: list[ArrowSegment]
lines: list[LineSegment]
frame_width: float
frame_height: float
def extract_scene(scene_class: type[Scene]) -> ParsedScene:
scene = scene_class()
scene.construct()
labels: list[Label] = []
arrows: list[ArrowSegment] = []
lines: list[LineSegment] = []
arrow_idx = 0
line_idx = 0
def walk(mobject):
nonlocal arrow_idx, line_idx
if isinstance(mobject, (Text, MathTex)):
ul = mobject.get_corner(UP + LEFT)
dr = mobject.get_corner(DOWN + RIGHT)
center = mobject.get_center()
# Text content: .text for Text, tex_string for MathTex
if isinstance(mobject, Text):
name = mobject.text
else:
name = mobject.tex_string
labels.append(Label(
name=name,
bbox=BBox(
x_min=float(ul[0]),
y_min=float(dr[1]),
x_max=float(dr[0]),
y_max=float(ul[1]),
),
center=Point(x=float(center[0]), y=float(center[1])),
))
return # Text/MathTex don't have meaningful children
if isinstance(mobject, Arrow):
start = mobject.get_start()
end = mobject.get_end()
arrows.append(ArrowSegment(
name=f"arrow_{arrow_idx}",
start=Point(x=float(start[0]), y=float(start[1])),
end=Point(x=float(end[0]), y=float(end[1])),
))
arrow_idx += 1
return # Don't recurse into Arrow internals
if isinstance(mobject, (Line, DashedLine)):
start = mobject.get_start()
end = mobject.get_end()
lines.append(LineSegment(
name=f"line_{line_idx}",
start=Point(x=float(start[0]), y=float(start[1])),
end=Point(x=float(end[0]), y=float(end[1])),
))
line_idx += 1
return
if isinstance(mobject, Polygon):
vertices = mobject.get_vertices()
for i in range(len(vertices)):
v1 = vertices[i]
v2 = vertices[(i + 1) % len(vertices)]
lines.append(LineSegment(
name=f"polygon_edge_{line_idx}",
start=Point(x=float(v1[0]), y=float(v1[1])),
end=Point(x=float(v2[0]), y=float(v2[1])),
))
line_idx += 1
return
# Recurse into container mobjects (VGroup, etc.)
for child in mobject.submobjects:
walk(child)
for mobject in scene.mobjects:
walk(mobject)
return ParsedScene(
scene_name=scene_class.__name__,
labels=labels,
arrows=arrows,
lines=lines,
frame_width=float(config.frame_width),
frame_height=float(config.frame_height),
)Step 4: Run test to verify it passes
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::test_extract_scene_returns_parsed_scene -v
Expected: PASS
Step 5: Commit
git add docs/diagrams/manim_quality.py docs/diagrams/test_manim_quality.py
git commit -m "feat: add manim scene-graph extractor and data model"Task 2: check_label_overlaps
Files:
- Modify:
docs/diagrams/manim_quality.py - Modify:
docs/diagrams/test_manim_quality.py
Step 1: Write the failing test
Add to test_manim_quality.py:
from manim_quality import check_label_overlaps
class OverlappingLabelsScene(Scene):
"""Two labels at the same position — must trigger overlap check."""
def construct(self):
self.add(Text("Hello", font_size=36).move_to(np.array([0.0, 0.0, 0.0])))
self.add(Text("World", font_size=36).move_to(np.array([0.0, 0.0, 0.0])))
class SeparatedLabelsScene(Scene):
"""Two labels far apart — must NOT trigger overlap check."""
def construct(self):
self.add(Text("Left", font_size=28).move_to(np.array([-5.0, 0.0, 0.0])))
self.add(Text("Right", font_size=28).move_to(np.array([5.0, 0.0, 0.0])))
def test_check_label_overlaps_detects_overlapping():
scene = extract_scene(OverlappingLabelsScene)
errors = check_label_overlaps(scene)
assert len(errors) >= 1
assert "Hello" in errors[0] and "World" in errors[0]
def test_check_label_overlaps_passes_separated():
scene = extract_scene(SeparatedLabelsScene)
errors = check_label_overlaps(scene)
assert len(errors) == 0Step 2: Run test to verify it fails
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::test_check_label_overlaps_detects_overlapping -v
Expected: FAIL with ImportError: cannot import name 'check_label_overlaps'
Step 3: Write minimal implementation
Add to manim_quality.py:
def check_label_overlaps(
scene: ParsedScene,
*,
padding: float = 0.05,
) -> list[str]:
errors: list[str] = []
for i, a in enumerate(scene.labels):
for b in scene.labels[i + 1:]:
if a.bbox.overlaps(b.bbox, padding=padding):
errors.append(
f"Labels overlap: '{a.name}' vs '{b.name}'"
)
return errorsStep 4: Run tests to verify they pass
Run: cd docs/diagrams && uv run pytest test_manim_quality.py -k "label_overlaps" -v
Expected: 2 PASS
Step 5: Commit
git add docs/diagrams/manim_quality.py docs/diagrams/test_manim_quality.py
git commit -m "feat: add check_label_overlaps to manim quality testkit"Task 3: check_arrow_crosses_label
Files:
- Modify:
docs/diagrams/manim_quality.py - Modify:
docs/diagrams/test_manim_quality.py
Step 1: Write the failing test
Add to test_manim_quality.py:
from manim_quality import check_arrow_crosses_label
class ArrowCrossesLabelScene(Scene):
"""Arrow passes directly through a text label."""
def construct(self):
self.add(Text("Target", font_size=36).move_to(np.array([1.0, 0.5, 0.0])))
self.add(Arrow(np.array([0, 0, 0.0]), np.array([2, 1, 0.0]), buff=0))
class ArrowMissesLabelScene(Scene):
"""Arrow and label are far apart."""
def construct(self):
self.add(Text("Safe", font_size=28).move_to(np.array([0.0, 3.0, 0.0])))
self.add(Arrow(np.array([0, -2, 0.0]), np.array([2, -2, 0.0]), buff=0))
def test_check_arrow_crosses_label_detects_crossing():
scene = extract_scene(ArrowCrossesLabelScene)
errors = check_arrow_crosses_label(scene)
assert len(errors) >= 1
assert "Target" in errors[0]
def test_check_arrow_crosses_label_passes_separated():
scene = extract_scene(ArrowMissesLabelScene)
errors = check_arrow_crosses_label(scene)
assert len(errors) == 0Step 2: Run test to verify it fails
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::test_check_arrow_crosses_label_detects_crossing -v
Expected: FAIL with ImportError
Step 3: Write minimal implementation
Add to manim_quality.py:
def check_arrow_crosses_label(
scene: ParsedScene,
*,
padding: float = 0.08,
) -> list[str]:
errors: list[str] = []
for arrow in scene.arrows:
arrow_line = LineString([
(arrow.start.x, arrow.start.y),
(arrow.end.x, arrow.end.y),
])
for label in scene.labels:
label_poly = label.bbox.to_shapely(padding=padding)
if arrow_line.intersects(label_poly):
errors.append(
f"Arrow {arrow.name} crosses label '{label.name}'"
)
return errorsStep 4: Run tests to verify they pass
Run: cd docs/diagrams && uv run pytest test_manim_quality.py -k "arrow_crosses" -v
Expected: 2 PASS
Step 5: Commit
git add docs/diagrams/manim_quality.py docs/diagrams/test_manim_quality.py
git commit -m "feat: add check_arrow_crosses_label to manim quality testkit"Task 4: check_outside_frame
Files:
- Modify:
docs/diagrams/manim_quality.py - Modify:
docs/diagrams/test_manim_quality.py
Step 1: Write the failing test
Add to test_manim_quality.py:
from manim_quality import check_outside_frame
class OutOfFrameScene(Scene):
"""Label placed well beyond the visible frame."""
def construct(self):
self.add(Text("Lost", font_size=28).move_to(np.array([20.0, 0.0, 0.0])))
class InsideFrameScene(Scene):
"""Everything comfortably inside the frame."""
def construct(self):
self.add(Text("Safe", font_size=28).move_to(np.array([0.0, 0.0, 0.0])))
self.add(Arrow(np.array([-2, 0, 0.0]), np.array([2, 0, 0.0]), buff=0))
def test_check_outside_frame_detects_clipping():
scene = extract_scene(OutOfFrameScene)
errors = check_outside_frame(scene)
assert len(errors) >= 1
assert "Lost" in errors[0]
def test_check_outside_frame_passes_inside():
scene = extract_scene(InsideFrameScene)
errors = check_outside_frame(scene)
assert len(errors) == 0Step 2: Run test to verify it fails
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::test_check_outside_frame_detects_clipping -v
Expected: FAIL with ImportError
Step 3: Write minimal implementation
Add to manim_quality.py:
def check_outside_frame(
scene: ParsedScene,
*,
margin: float = 0.1,
) -> list[str]:
errors: list[str] = []
hw = scene.frame_width / 2 - margin
hh = scene.frame_height / 2 - margin
for label in scene.labels:
bb = label.bbox
if bb.x_min < -hw or bb.x_max > hw or bb.y_min < -hh or bb.y_max > hh:
errors.append(
f"Label '{label.name}' extends outside frame"
)
for arrow in scene.arrows:
for pt_name, pt in [("start", arrow.start), ("end", arrow.end)]:
if abs(pt.x) > hw or abs(pt.y) > hh:
errors.append(
f"Arrow {arrow.name} {pt_name} outside frame "
f"({pt.x:.2f}, {pt.y:.2f})"
)
return errorsStep 4: Run tests to verify they pass
Run: cd docs/diagrams && uv run pytest test_manim_quality.py -k "outside_frame" -v
Expected: 2 PASS
Step 5: Commit
git add docs/diagrams/manim_quality.py docs/diagrams/test_manim_quality.py
git commit -m "feat: add check_outside_frame to manim quality testkit"Task 5: check_line_near_parallel
Files:
- Modify:
docs/diagrams/manim_quality.py - Modify:
docs/diagrams/test_manim_quality.py
Step 1: Write the failing test
Add to test_manim_quality.py:
from manim import DashedLine
from manim_quality import check_line_near_parallel
class ParallelLinesScene(Scene):
"""Two lines almost on top of each other — visually indistinguishable."""
def construct(self):
self.add(Line(np.array([0, 0, 0.0]), np.array([3, 0, 0.0])))
self.add(Arrow(np.array([0, 0.05, 0.0]), np.array([3, 0.05, 0.0]), buff=0))
class PerpendicularLinesScene(Scene):
"""Two lines at 90 degrees — no parallelism issue."""
def construct(self):
self.add(Line(np.array([0, 0, 0.0]), np.array([3, 0, 0.0])))
self.add(Arrow(np.array([1.5, -1, 0.0]), np.array([1.5, 1, 0.0]), buff=0))
def test_check_line_near_parallel_detects_parallel():
scene = extract_scene(ParallelLinesScene)
errors = check_line_near_parallel(scene)
assert len(errors) >= 1
def test_check_line_near_parallel_passes_perpendicular():
scene = extract_scene(PerpendicularLinesScene)
errors = check_line_near_parallel(scene)
assert len(errors) == 0Step 2: Run test to verify it fails
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::test_check_line_near_parallel_detects_parallel -v
Expected: FAIL with ImportError
Step 3: Write minimal implementation
Add to manim_quality.py:
def _midpoint(start: Point, end: Point) -> Point:
return Point(x=(start.x + end.x) / 2, y=(start.y + end.y) / 2)
def _direction_angle(start: Point, end: Point) -> float:
"""Angle in degrees, normalized to [0, 180)."""
dx = end.x - start.x
dy = end.y - start.y
angle = math.degrees(math.atan2(dy, dx)) % 180
return angle
def _point_to_line_distance(
pt: Point,
line_start: Point,
line_end: Point,
) -> float:
"""Perpendicular distance from pt to the infinite line through start→end."""
dx = line_end.x - line_start.x
dy = line_end.y - line_start.y
length = math.hypot(dx, dy)
if length < 1e-9:
return math.hypot(pt.x - line_start.x, pt.y - line_start.y)
return abs(dy * pt.x - dx * pt.y + line_end.x * line_start.y - line_end.y * line_start.x) / length
def check_line_near_parallel(
scene: ParsedScene,
*,
distance: float = 0.15,
angle_degrees: float = 10.0,
) -> list[str]:
errors: list[str] = []
all_segments: list[tuple[str, Point, Point]] = []
for arrow in scene.arrows:
all_segments.append((arrow.name, arrow.start, arrow.end))
for line in scene.lines:
all_segments.append((line.name, line.start, line.end))
for i, (name_a, start_a, end_a) in enumerate(all_segments):
angle_a = _direction_angle(start_a, end_a)
mid_a = _midpoint(start_a, end_a)
for name_b, start_b, end_b in all_segments[i + 1:]:
angle_b = _direction_angle(start_b, end_b)
angle_diff = abs(angle_a - angle_b)
if angle_diff > 90:
angle_diff = 180 - angle_diff
if angle_diff > angle_degrees:
continue
dist = _point_to_line_distance(mid_a, start_b, end_b)
if dist < distance:
errors.append(
f"Near-parallel: {name_a} and {name_b} "
f"(distance={dist:.3f}, angle_diff={angle_diff:.1f}°)"
)
return errorsStep 4: Run tests to verify they pass
Run: cd docs/diagrams && uv run pytest test_manim_quality.py -k "near_parallel" -v
Expected: 2 PASS
Step 5: Commit
git add docs/diagrams/manim_quality.py docs/diagrams/test_manim_quality.py
git commit -m "feat: add check_line_near_parallel to manim quality testkit"Task 6: run_all_checks aggregator and TestFixturesMustFail
Files:
- Modify:
docs/diagrams/manim_quality.py - Modify:
docs/diagrams/test_manim_quality.py
Step 1: Write the failing test
Add to test_manim_quality.py:
import pytest
from manim_quality import run_all_checks
BAD_SCENES = [
OverlappingLabelsScene,
ArrowCrossesLabelScene,
OutOfFrameScene,
ParallelLinesScene,
]
class TestFixturesMustFail:
@pytest.fixture(params=BAD_SCENES, ids=lambda s: s.__name__)
def bad_scene(self, request):
return extract_scene(request.param)
def test_fixture_fails_at_least_one_check(self, bad_scene):
errors = run_all_checks(bad_scene)
assert errors, (
f"{bad_scene.scene_name} passed ALL checks — "
f"the checks are too lenient"
)Step 2: Run test to verify it fails
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::TestFixturesMustFail -v
Expected: FAIL with ImportError: cannot import name 'run_all_checks'
Step 3: Write minimal implementation
Add to manim_quality.py:
def run_all_checks(scene: ParsedScene) -> list[str]:
errors: list[str] = []
errors.extend(check_label_overlaps(scene))
errors.extend(check_arrow_crosses_label(scene))
errors.extend(check_outside_frame(scene))
errors.extend(check_line_near_parallel(scene))
return errorsStep 4: Run tests to verify they pass
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::TestFixturesMustFail -v
Expected: 4 PASS (one per bad scene)
Step 5: Commit
git add docs/diagrams/manim_quality.py docs/diagrams/test_manim_quality.py
git commit -m "feat: add run_all_checks and TestFixturesMustFail"Task 7: Parametrized tests for real diagram scenes
Files:
- Modify:
docs/diagrams/test_manim_quality.py
Important: Real scenes use MathTex which requires LaTeX. The tests must skip gracefully when LaTeX is unavailable.
Step 1: Write the tests
Add to test_manim_quality.py:
import shutil
# Import the real scenes — this import works even without LaTeX
from multivariable_calculus_manim import ( # uses underscore for import
CoordinateFreeCentroid,
ArcLengthComparison,
GradientPerpendicularToLevelCurves,
CriticalPointTypes,
LagrangeTangentLevelCurves,
)
HAS_LATEX = shutil.which("latex") is not None
REAL_SCENES = [
CoordinateFreeCentroid,
ArcLengthComparison,
GradientPerpendicularToLevelCurves,
CriticalPointTypes,
LagrangeTangentLevelCurves,
]
@pytest.fixture(params=REAL_SCENES, ids=lambda s: s.__name__)
def real_scene(request):
if not HAS_LATEX:
pytest.skip("LaTeX not available — MathTex scenes need latex binary")
return extract_scene(request.param)
class TestDiagramQuality:
def test_no_label_overlaps(self, real_scene):
errors = check_label_overlaps(real_scene)
assert not errors, "\n".join(errors)
def test_no_arrow_crosses_label(self, real_scene):
errors = check_arrow_crosses_label(real_scene)
assert not errors, "\n".join(errors)
def test_nothing_outside_frame(self, real_scene):
errors = check_outside_frame(real_scene)
assert not errors, "\n".join(errors)
def test_no_near_parallel_lines(self, real_scene):
errors = check_line_near_parallel(real_scene)
assert not errors, "\n".join(errors)Note on imports: The manim script is named multivariable-calculus-manim.py (with hyphens). Python can’t import filenames with hyphens. Two options:
- Rename to
multivariable_calculus_manim.py(breaking change for existing references) - Use
importlibto load it
The plan uses importlib to avoid renaming:
import importlib.util
def _import_manim_scenes():
spec = importlib.util.spec_from_file_location(
"manim_scenes",
Path(__file__).parent / "multivariable-calculus-manim.py",
)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
_scenes_mod = _import_manim_scenes()
CoordinateFreeCentroid = _scenes_mod.CoordinateFreeCentroid
# ... etcStep 2: Run tests
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::TestDiagramQuality -v
Expected: If LaTeX available, tests run (some may FAIL on current diagrams — that’s expected, the centroid diagram has known overlaps). If no LaTeX, all 20 tests SKIP.
Step 3: Commit
git add docs/diagrams/test_manim_quality.py
git commit -m "feat: add parametrized quality tests for real diagram scenes"Task 8: Update Dagger CI to run quality checks first
Files:
- Modify:
dagger/main.go— updatevalidateDiagramsfunction
Step 1: Modify the Dagger function
The validateDiagrams function currently only does pixel-diff. Add a quality-check step before rendering. Update the pip install line to include shapely and pytest. Add a pytest exec before the render-and-compare exec:
In dagger/main.go, change the pip install line from:
WithExec([]string{"pip", "install", "manim"}).to:
WithExec([]string{"pip", "install", "manim", "shapely", "pytest"}).And insert a new WithExec call before the render bash script:
ctr = ctr.WithExec([]string{
"python", "-m", "pytest",
"docs/diagrams/test_manim_quality.py", "-v",
"--tb=short",
})Step 2: Verify Go compiles
Run: cd dagger && go build -o /dev/null .
Expected: clean compile
Step 3: Commit
git add dagger/main.go
git commit -m "ci: run manim quality checks before pixel-diff in validate-diagrams"Task 9: Fix the centroid diagram coordinates
Files:
- Modify:
docs/diagrams/multivariable-calculus-manim.py—CoordinateFreeCentroidclass
Context: The current diagram has known quality issues (overlapping labels, arrow running along edge). After the testkit is in place, fix the coordinates so the tests pass.
Step 1: Run quality tests to identify failures
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::TestDiagramQuality -v (requires LaTeX)
Step 2: Fix coordinates in CoordinateFreeCentroid
Key changes needed:
- Move origin O further out (e.g.
[-5.5, -3.0, 0.0]) so position vectors fan out without overlapping triangle edges - Widen the triangle asymmetrically (e.g.
a=[-3.5, -1.5, 0],b=[4.0, -1.5, 0],c=[0.5, 2.5, 0]) so medians and labels have room - Adjust label offsets based on actual geometry rather than hardcoded nudges
- Place centroid label further from midpoint M
Step 3: Run quality tests to verify fixes
Run: cd docs/diagrams && uv run pytest test_manim_quality.py -k CoordinateFreeCentroid -v
Expected: PASS on all 4 checks
Step 4: Re-render the PNG (requires LaTeX)
Run: cd docs/diagrams && uv run python multivariable-calculus-manim.py or render individually via manim CLI.
Step 5: Commit
git add docs/diagrams/multivariable-calculus-manim.py "2 - Areas/Math, Geometry, Engineering, Physics/Math/Calculus/diagrams/coordinate-free-centroid.png"
git commit -m "fix: redesign centroid diagram coordinates to pass quality checks"Task 10: Fix remaining 4 diagram scenes
Files:
- Modify:
docs/diagrams/multivariable-calculus-manim.py
Step 1: Run quality tests on all scenes
Run: cd docs/diagrams && uv run pytest test_manim_quality.py::TestDiagramQuality -v
Step 2: Fix any failing scenes
For each scene that fails, adjust coordinates and label positions until checks pass. Run tests between each change.
Step 3: Re-render all PNGs
Run the manim render script to regenerate all 5 PNGs.
Step 4: Commit
git add docs/diagrams/multivariable-calculus-manim.py "2 - Areas/Math, Geometry, Engineering, Physics/Math/Calculus/diagrams/"
git commit -m "fix: adjust all diagram coordinates to pass quality checks"Task 11: Run full test suite and verify CI
Step 1: Run all tests locally
Run: cd docs/diagrams && uv run pytest test_manim_quality.py -v
Expected: All tests PASS (must-fail fixtures fail correctly, real scenes pass quality checks, or skip if no LaTeX)
Step 2: Run Dagger validate-diagrams locally
Run: cd dagger && go run . validate-diagrams (requires Docker + LaTeX in container)
Expected: Quality checks pass, then pixel-diff passes
Step 3: Commit any final adjustments and push
git push gitlab docs/multivariable-calculus-foundations