Corpus and adversarial evidence¶
Measurements taken before promoting qual from a developer tool to a pre-execution admission check for a hosted render service. They record what the analyzer actually did on real third-party sources and on inputs chosen to break it — not what its own test suite asserts.
Snapshot¶
- Captured: 2026-07-28 (Asia/Tokyo)
- Analyzer:
feat/admission-readiness, built from the tree that addssrc/source_limits.rs - Corpus:
Poietra/fast-manimatmain(an independent performance fork of Manim Community 0.20), 393 Python files, 5.6 MB - Also analyzed: the four Manim sources in
Poietra/studio-lab(examples/relativity.py,server/test-fixtures/real-manim-smoke.py,fixtures/preview-harness/shared_circle_opacity.py,fixtures/real-preview-harness/scene.py) - Machine: WSL2, Linux 6.6.87.2, x86-64, 16 logical CPUs
Corpus results¶
| Target | Files | Findings | Wall time | Process outcome |
|---|---|---|---|---|
example_scenes/ |
4 | 1 (MLP208, info) |
0.18 s | clean exit |
tests/ |
200 | 22 | 5.99 s | clean exit |
| whole repository | 393 | 46 | 26.9 s | clean exit |
| studio-lab Manim sources | 4 | 0 | < 0.2 s | clean exit |
No crash, no panic, no stderr output, and no MLC000 on any corpus file.
Triage of every error-severity finding¶
All 17 error-severity findings in tests/ were inspected against their source
lines. None is a false positive, but only some are actionable:
- 13 are inside
pytest.raises/try ... exceptblocks that deliberately construct the invalid call the rule describes (VGroup(3.0),obj.add(Mobject()),AnimationGroup()): the diagnostic is factually correct, and the test proves it by asserting the same failure. - 2 (
MLR105, unbalanced Pango markup) are the same pattern written with a baretryinstead ofpytest.raises. - 1 (
MLC105, an updater whose parameters cannot bind) is in a scene fixture. - 1 (
MLP206) flags a 1 nswait, which is a test artifact.
The 5 library-side MLC126 findings (probability.py, code_mobject.py,
three_dimensions.py, debug.py) each sit on a call whose child is a value the
analyzer cannot conclusively type, and each is reported at high — not
certain — confidence.
Consequence for admission use: a gate keyed on severity alone would reject
correct source that intentionally exercises failure paths. An admission check
must key on --min-confidence certain and observe high findings before ever
blocking on them. That is the gradual path documented in the README.
False-positive probe¶
A scene using project-local subclasses (class LocalBox(VGroup)), an unresolved
third-party import, and helper-returned mobjects produced no findings — the
conclusive requirement on negative claims held: unresolved bases stay silent
rather than guessing.
Adversarial results¶
Inputs were generated to attack the parser and analysis passes, not the rules.
Before the limits in src/source_limits.rs, four of eight aborted the process:
| Input | Before | After |
|---|---|---|
| 100 000 nested parentheses | clean exit | MLC000 bracket depth |
4 000 nested VGroup( calls |
stack overflow, SIGABRT | MLC000 bracket depth |
20 000 nested VGroup( calls |
stack overflow, SIGABRT | MLC000 bracket depth |
5 000 nested if blocks (50 MB) |
stack overflow, SIGABRT | MLC000 source size |
| 12 MB / 2 000 000 statements | > 30 s, no result | MLC000 source size |
50 000 unary - operators |
stack overflow, SIGABRT | MLC000 prefix run |
30 000 - split by line continuations |
stack overflow, SIGABRT | MLC000 prefix run |
50 000 chained not |
stack overflow, SIGABRT | MLC000 prefix run |
A stack overflow in a Rust process is an abort, not a catchable error: no
catch_unwind and no per-file isolation can contain it. The only safe response
is to refuse the input before the recursion starts, which is what the pre-parse
check does.
Nesting thresholds were located by bisection (1 000 nested calls survived, 4 000 did not; 2 000 nested blocks survived, 5 000 did not). They are stack-size and build dependent, so the enforced bounds are set an order of magnitude below the lowest observed failure rather than at it.
Headroom against real code¶
| Measure | Corpus maximum | Enforced limit | Headroom |
|---|---|---|---|
| bracket nesting depth | 15 | 96 | 6.4× |
| indentation depth | 13 (53 leading spaces) | 96 | 7.4× |
| file size | 419 KB | 4 MiB | 10× |
Re-running the whole corpus after the limits produced the identical 46
findings and zero MLC000, so no real file is affected.
Not yet evidenced¶
- Corpus breadth is one fork plus one consumer. Third-party Manim projects written by other authors have not been measured.
- No fuzzing campaign: the adversarial inputs are hand-constructed classes of
attack, not a coverage-guided search.
cargo-fuzzoverSourceManagerremains open work. - The prefix-run and depth bounds are empirical for this build. A release-mode binary, another platform, or a thread with a different stack size shifts the true failure point; the bounds are conservative but not derived from a proof.
- Analysis time is bounded only by the source-size limit. A wall-clock budget per invocation is the caller's responsibility.