Catch missing drawings, accidental exposure gaps, and conflicting cells before a 2D-animation handoff reaches compositing.
XSheetProof compares a portable JSON exposure sheet with the files actually delivered. It expands every layer into exact frame cells and writes two reviewable artifacts: a deterministic JSON manifest and a standalone, script-free HTML exposure report. It never edits source files and does not need an account, editor plug-in, or network connection.
An exposure sheet can say drawing A002 is held on frames 4–6 while the delivery folder
does not contain that drawing. A layer can also hide an undeclared gap or contain two exposures
that claim the same frame. File-list checks cannot see those timeline errors, and timeline
editors do not necessarily prove what was placed in the handoff folder.
XSheetProof joins those two facts in one reproducible preflight:
scene.json + delivered drawing files
-> strict input validation
-> per-layer, per-frame exposure expansion
-> actionable diagnostics + manifest.json + report.html
It is designed for small animation teams, students, cleanup/in-betweening vendors, technical directors, and anyone reviewing a shot handoff outside the originating animation application.
- Frames with no declared exposure on a layer.
- Frames claimed by more than one exposure on the same layer.
- Exposures and cues outside the scene range.
- Missing, empty, non-file, or unreadable referenced assets.
- Unsafe asset paths and schema mistakes before any output is created.
- Intentional empty cells as an explicit, valid
"blank": trueexposure.
Python 3.11 or newer is required. Install the published v0.1.0 wheel directly from the GitHub Release with uv:
uv tool install https://github.com/KanadeK/xsheetproof/releases/download/v0.1.0/xsheetproof-0.1.0-py3-none-any.whl
xsheetproof versionExpected version output:
xsheetproof 0.1.0
To work from source instead:
git clone https://github.com/KanadeK/xsheetproof.git
cd xsheetproof
uv sync --locked --devFrom the cloned repository, check the included delivery and create a new proof directory:
uv run xsheetproof check examples/clean/scene.json --out proofExpected terminal result:
CLEAN: SC010; 6 frames, 2 layers; wrote 2 files
Open proof/report.html in any browser to review the exposure table, or consume
proof/manifest.json in another tool. Re-running against the same output directory fails
instead of overwriting evidence; choose a new directory or remove the old one deliberately.
Paths are relative to the JSON file. Exposure ranges are inclusive, and every layer must cover the complete scene range. This two-frame sheet points to one real delivered file:
{
"schema_version": 1,
"scene": {
"id": "SC020",
"fps": 24,
"start_frame": 1,
"end_frame": 2
},
"layers": [
{
"name": "character",
"exposures": [
{
"start": 1,
"end": 2,
"drawing": "A001",
"asset": "drawings/A001.svg"
}
]
}
]
}Use an explicit blank when a layer is intentionally empty:
{
"start": 1,
"end": 2,
"drawing": "BLANK",
"blank": true
}See the complete format reference and the committed
intentional-blank example.
Every structurally valid sheet writes exactly these files:
manifest.json— stable, machine-readable scene metadata, diagnostics, layers, cues, and every expanded frame cell.report.html— one self-contained, escaped, script-free report for human review.
The process exit code makes XSheetProof usable in scripts and CI:
| Exit | Meaning | Output bundle |
|---|---|---|
0 |
The sheet and delivered files are clean. | Written |
1 |
The sheet is valid but blocking handoff diagnostics exist. | Written |
2 |
The input, command, or output destination is invalid. | Not created |
For example, the committed missing-file handoff returns 1 while preserving evidence:
uv run xsheetproof check examples/missing-asset/scene.json --out missing-proofBLOCKED: SC013; 1 diagnostic; wrote 2 files
Its manifest identifies ASSET_MISSING, the exact exposure path, and this repair: copy the
drawing into the sheet directory or correct the asset path. See
troubleshooting for every diagnostic and input failure.
| Example | Expected result | What it demonstrates |
|---|---|---|
clean |
Exit 0 |
Two fully covered layers, real SVG files, and a dialogue cue. |
intentional-blank |
Exit 0 |
A fully empty effects layer declared without ambiguity. |
overlap |
Exit 1 |
Both competing drawings remain visible on frames 2–3. |
missing-asset |
Exit 1 |
A valid sheet that references an absent delivery file. |
The repository has one acceptance command, shared by local development and CI:
uv sync --locked --dev
uv run python scripts/check.pyIt verifies formatting, lint, strict types, branch-covered tests, all four examples,
deterministic output, wheel and sdist construction, clean wheel installation, the installed
console entry point, and the README quick start. A successful run ends with CHECK: PASS.
Contributions are welcome through the focused workflow in CONTRIBUTING.md. Security reports follow SECURITY.md.
- JSON is the only input format in v0.1.0; there are no OpenToonz, Toon Boom, Aseprite, or OpenTimelineIO adapters.
- Asset checks prove safe resolution, regular-file presence, readability, and non-zero size. XSheetProof does not decode images or judge drawing, lip-sync, or render quality.
- One input contains one scene with at most 10,000 frames, 64 layers, 20,000 exposures, 10,000 cues, and 2 MiB of JSON.
- XSheetProof reports problems but never repairs a sheet, renames drawings, renders frames, compares artwork, analyzes audio, or uploads data.
These boundaries are intentional: v0.1.0 is a small, deterministic handoff preflight rather than another animation editor.
MIT © KanadeK.