lenkraster 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. lenkraster-0.1.0/ASSET_LICENSE.md +34 -0
  2. lenkraster-0.1.0/CHANGELOG.md +43 -0
  3. lenkraster-0.1.0/CONTRIBUTING.md +44 -0
  4. lenkraster-0.1.0/LICENSE +20 -0
  5. lenkraster-0.1.0/MANIFEST.in +19 -0
  6. lenkraster-0.1.0/PKG-INFO +412 -0
  7. lenkraster-0.1.0/PYPI_README.md +375 -0
  8. lenkraster-0.1.0/README.md +375 -0
  9. lenkraster-0.1.0/SECURITY.md +52 -0
  10. lenkraster-0.1.0/THIRD_PARTY_NOTICES.md +41 -0
  11. lenkraster-0.1.0/docs/examples/README.md +21 -0
  12. lenkraster-0.1.0/docs/name-clearance.md +29 -0
  13. lenkraster-0.1.0/docs/release-checklist.md +59 -0
  14. lenkraster-0.1.0/mcp/lenkraster_mcp.py +15 -0
  15. lenkraster-0.1.0/pyproject.toml +53 -0
  16. lenkraster-0.1.0/requirements/ci.txt +10 -0
  17. lenkraster-0.1.0/requirements/release.in +10 -0
  18. lenkraster-0.1.0/requirements/release.txt +710 -0
  19. lenkraster-0.1.0/scripts/audit_legal_content.py +189 -0
  20. lenkraster-0.1.0/scripts/audit_public_tree.py +201 -0
  21. lenkraster-0.1.0/setup.cfg +4 -0
  22. lenkraster-0.1.0/skills/pixel-color-ramps/SKILL.md +47 -0
  23. lenkraster-0.1.0/skills/pixel-craft-critique/SKILL.md +51 -0
  24. lenkraster-0.1.0/skills/references/craft-rules.md +98 -0
  25. lenkraster-0.1.0/skills/references/motion-timing.md +62 -0
  26. lenkraster-0.1.0/src/lenkraster/__init__.py +23 -0
  27. lenkraster-0.1.0/src/lenkraster/aseprite.py +815 -0
  28. lenkraster-0.1.0/src/lenkraster/cli.py +237 -0
  29. lenkraster-0.1.0/src/lenkraster/critic.py +539 -0
  30. lenkraster-0.1.0/src/lenkraster/cycle.py +413 -0
  31. lenkraster-0.1.0/src/lenkraster/data/palettes/lenk-cinder-16.json +1 -0
  32. lenkraster-0.1.0/src/lenkraster/data/palettes/lenk-fern-4.json +1 -0
  33. lenkraster-0.1.0/src/lenkraster/data/palettes/lenk-signal-16.json +1 -0
  34. lenkraster-0.1.0/src/lenkraster/data/palettes/lenk-studio-32.json +1 -0
  35. lenkraster-0.1.0/src/lenkraster/mcp_main.py +7 -0
  36. lenkraster-0.1.0/src/lenkraster/mcp_server.py +931 -0
  37. lenkraster-0.1.0/src/lenkraster/palette.py +428 -0
  38. lenkraster-0.1.0/src/lenkraster/shadow.py +644 -0
  39. lenkraster-0.1.0/src/lenkraster.egg-info/PKG-INFO +412 -0
  40. lenkraster-0.1.0/src/lenkraster.egg-info/SOURCES.txt +52 -0
  41. lenkraster-0.1.0/src/lenkraster.egg-info/dependency_links.txt +1 -0
  42. lenkraster-0.1.0/src/lenkraster.egg-info/entry_points.txt +3 -0
  43. lenkraster-0.1.0/src/lenkraster.egg-info/requires.txt +11 -0
  44. lenkraster-0.1.0/src/lenkraster.egg-info/top_level.txt +1 -0
  45. lenkraster-0.1.0/tests/test_aseprite.py +433 -0
  46. lenkraster-0.1.0/tests/test_aseprite_real.py +128 -0
  47. lenkraster-0.1.0/tests/test_cli.py +246 -0
  48. lenkraster-0.1.0/tests/test_critic.py +298 -0
  49. lenkraster-0.1.0/tests/test_cycle.py +499 -0
  50. lenkraster-0.1.0/tests/test_mcp.py +764 -0
  51. lenkraster-0.1.0/tests/test_mcp_wheel.py +194 -0
  52. lenkraster-0.1.0/tests/test_palette.py +369 -0
  53. lenkraster-0.1.0/tests/test_release_configuration.py +234 -0
  54. lenkraster-0.1.0/tests/test_shadow.py +507 -0
@@ -0,0 +1,34 @@
1
+ # Asset license
2
+
3
+ Copyright (c) 2026 Ryan Lenk.
4
+
5
+ Unless a file states otherwise, every repository-authored visual asset, palette JSON,
6
+ example fixture, and documentation image in this repository is licensed under the
7
+ MIT License in [`LICENSE`](LICENSE), to the extent the material is protected by copyright.
8
+ This permission includes the four built-in palettes:
9
+
10
+ - Lenk Cinder 16
11
+ - Lenk Fern 4
12
+ - Lenk Signal 16
13
+ - Lenk Studio 32
14
+
15
+ Those palettes were created specifically for LenkRaster from independently selected RGB
16
+ values. They are not derived from or compatible-name copies of a third-party palette.
17
+ Their `author` field identifies Ryan Lenk. The exact release-candidate files are pinned
18
+ here so later changes are visible:
19
+
20
+ | File | SHA-256 |
21
+ |---|---|
22
+ | `lenk-cinder-16.json` | `ce0fbb4c6e96b814356da83e23bb1fac536026a5bab198449008d1e37bf666d1` |
23
+ | `lenk-fern-4.json` | `9e3a08c13a530ce442085c6d724e57cd61f198db9bb456124fe2e25973a60683` |
24
+ | `lenk-signal-16.json` | `07eb4c2220e3cf72be559907ffc1dcc6d92401bf44b8716fe350e610018bd8fb` |
25
+ | `lenk-studio-32.json` | `d5570602f593d4d2645d142c81b06cb38892b85eb5873b36bde95915fc68f51a` |
26
+
27
+ The demonstration artwork was created at Ryan Lenk's direction with OpenAI image-generation
28
+ tools and without an external character, logo, or artwork reference. The license grant
29
+ above applies to any rights Ryan Lenk can license in those outputs. It does not claim
30
+ exclusive rights in unprotectable, public-domain, or third-party material.
31
+
32
+ The embedded content-credentials metadata in generated images is provenance information,
33
+ not an additional license or endorsement. Aseprite was used only as an operator-installed
34
+ editing/export tool and is not bundled with these assets.
@@ -0,0 +1,43 @@
1
+ # Changelog
2
+
3
+ All notable changes to LenkRaster will be documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and releases
6
+ use semantic versioning once the first public tag is created.
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Optional, bounded Aseprite CLI export and direct cycle-QA integration.
13
+ - Two Aseprite MCP tools and matching CLI/Python APIs.
14
+ - Public security policy, contributing guide, release checklist, dependency audit, and
15
+ secret/path artifact checks.
16
+
17
+ ### Changed
18
+
19
+ - Adopt the pre-release `LenkRaster` identity across the distribution, import package,
20
+ command-line entry points, local MCP server, repository links, and environment variables.
21
+ - Replace all previously bundled palettes with four original Ryan Lenk palettes and add
22
+ explicit asset licensing, provenance, third-party notices, and a name-screen record.
23
+
24
+ ### Security
25
+
26
+ - Require an explicit MCP trusted root for every tool call.
27
+ - Require Python 3.10+, Pillow 12.3+, and a current reproducible CI dependency set.
28
+ - Validate Aseprite metadata and PNG sheets before publishing or analysis.
29
+ - Keep Aseprite invocation fixed, non-shell, time-bounded, and create-only.
30
+ - Contain user-owned palette files below an explicit trusted root and validate their
31
+ schema, encoded size, metadata, color count, and unique RGB values before image work.
32
+
33
+ ### Removed
34
+
35
+ - Private project-specific calibration reports, adapter code, tests, and handoff material
36
+ from the public source tree.
37
+ - All externally authored palette data and ambiguous palette provenance; users may supply
38
+ lawfully obtained values through the bounded custom-palette interface.
39
+
40
+ ## [0.1.0] - Unreleased
41
+
42
+ - Initial bounded critic, palette lab, animation QA, golden-corpus runner, CLI, and local
43
+ stdio MCP server.
@@ -0,0 +1,44 @@
1
+ # Contributing
2
+
3
+ LenkRaster welcomes small, evidence-backed improvements to its deterministic pixel-art
4
+ engines and trusted-local interfaces.
5
+
6
+ ## Development setup
7
+
8
+ Use Python 3.10 or newer in an isolated environment:
9
+
10
+ ```console
11
+ python -m venv .venv
12
+ python -m pip install --upgrade pip
13
+ python -m pip install -c requirements/ci.txt -e ".[dev]"
14
+ python -m pytest tests -q
15
+ ```
16
+
17
+ Before submitting a change, also run:
18
+
19
+ ```console
20
+ python -m pip check
21
+ python -m pip_audit --skip-editable
22
+ python -m bandit -q -r src
23
+ python scripts/audit_public_tree.py
24
+ python -m build
25
+ python scripts/audit_public_tree.py dist
26
+ ```
27
+
28
+ ## Engineering expectations
29
+
30
+ - Reproduce defects with a failing test before changing implementation.
31
+ - Keep all file, image, animation, JSON, request, and subprocess work explicitly bounded.
32
+ - Preserve trusted-root containment, path-free public failures, and create-only outputs.
33
+ - Use argument vectors with `shell=False`; never accept arbitrary subprocess flags or
34
+ scripts through MCP.
35
+ - Keep the MCP server local stdio only.
36
+ - Preserve advisory semantics. A score or cycle result is not art approval.
37
+ - Add synthetic fixtures in tests. Never commit private, client-owned, or proprietary art.
38
+ - Do not vendor or redistribute Aseprite, its binaries, or licensed sample content.
39
+ - Do not add secrets, workstation paths, personal email addresses, or `.env` files.
40
+
41
+ ## Pull requests
42
+
43
+ Describe the original scenario, the new test, security-boundary impact, and the exact
44
+ verification performed. Keep unrelated refactors separate.
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ryan Lenk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20
+ OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,19 @@
1
+ include CHANGELOG.md
2
+ include ASSET_LICENSE.md
3
+ include CONTRIBUTING.md
4
+ include LICENSE
5
+ include README.md
6
+ include PYPI_README.md
7
+ include SECURITY.md
8
+ include THIRD_PARTY_NOTICES.md
9
+ recursive-include docs *.md
10
+ recursive-include mcp *.py
11
+ recursive-include requirements *.in *.txt
12
+ recursive-include scripts *.py
13
+ recursive-include skills *.md
14
+ recursive-include tests *.py
15
+ global-exclude *.py[cod]
16
+ global-exclude .DS_Store
17
+ prune .claude
18
+ prune build
19
+ prune dist
@@ -0,0 +1,412 @@
1
+ Metadata-Version: 2.4
2
+ Name: lenkraster
3
+ Version: 0.1.0
4
+ Summary: Bounded pixel-art critique, palette, animation QA, Aseprite, and local MCP tools.
5
+ Author: Ryan Lenk
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/itsryanlenk/lenkraster
8
+ Project-URL: Documentation, https://github.com/itsryanlenk/lenkraster#readme
9
+ Project-URL: Issues, https://github.com/itsryanlenk/lenkraster/issues
10
+ Project-URL: Security, https://github.com/itsryanlenk/lenkraster/security/policy
11
+ Keywords: pixel-art,sprites,aseprite,game-dev,mcp,color,critique
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Artistic Software
21
+ Classifier: Topic :: Games/Entertainment
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ License-File: ASSET_LICENSE.md
26
+ License-File: THIRD_PARTY_NOTICES.md
27
+ Requires-Dist: numpy<3,>=2.0
28
+ Requires-Dist: Pillow<13,>=12.3
29
+ Provides-Extra: mcp
30
+ Provides-Extra: dev
31
+ Requires-Dist: bandit==1.9.4; extra == "dev"
32
+ Requires-Dist: build==1.5.0; extra == "dev"
33
+ Requires-Dist: pip-audit==2.10.1; extra == "dev"
34
+ Requires-Dist: pytest==9.1.1; extra == "dev"
35
+ Requires-Dist: setuptools==84.0.0; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # LenkRaster
39
+
40
+ ![LenkRaster — bounded pixel-art tools](https://raw.githubusercontent.com/itsryanlenk/lenkraster/main/docs/assets/lenkraster-readme-banner.png)
41
+
42
+ LenkRaster is a deterministic, bounded toolkit for inspecting and preparing pixel art.
43
+ It combines craft critique, palette operations, animation-cycle QA, an optional Aseprite
44
+ CLI bridge, a command-line interface, a Python API, and a trusted-local stdio MCP server.
45
+
46
+ LenkRaster is designed for agent workflows without turning an agent into an art director:
47
+ its reports are advisory evidence and retry hints. It does not approve artwork, replace
48
+ human review, or replace a project's composition, provenance, and release gates.
49
+
50
+ ## What it does
51
+
52
+ - Scores bounded PNG sprites against explainable pixel-craft checks.
53
+ - Builds OKLCH hue-shifted material ramps.
54
+ - Quantizes PNGs against original built-ins or bounded user-owned palette JSON without
55
+ overwriting existing files.
56
+ - Reports same-hue contrast and palette structure.
57
+ - Measures animation motion and visibility using rendered-pixel math.
58
+ - Exports and directly checks trusted local `.ase`/`.aseprite` documents through a
59
+ separately installed Aseprite CLI.
60
+ - Exposes the same bounded operations to local MCP clients over stdio.
61
+ - Replays user-owned, SHA-256-pinned golden corpora without promoting results to approval.
62
+
63
+ No GPU, API key, model download, browser, network service, or persistent daemon is
64
+ required.
65
+
66
+ ## See it in action
67
+
68
+ These are real outputs from the current LenkRaster CLI. The public source art and its
69
+ provenance are documented in
70
+ [docs/examples](https://github.com/itsryanlenk/lenkraster/blob/main/docs/examples/README.md).
71
+
72
+ ### One fixed palette across an animation
73
+
74
+ Palette normalization is constraint enforcement for asset pipelines, not an attempt to
75
+ "improve" an artist's source image. This four-frame hammer cycle intentionally contains
76
+ frame-exclusive RGB variation. LenkRaster maps it to one repository-owned 48-color target
77
+ without changing the poses or overwriting the source.
78
+
79
+ | Source cycle | Fixed target | Verified output |
80
+ |---|---|---|
81
+ | 77,766 distinct visible RGB values | 48 user-owned colors | 48/48 used; every visible output color belongs to the target |
82
+
83
+ ![Four-frame blacksmith automaton hammer cycle mapped to one fixed 48-color palette](https://raw.githubusercontent.com/itsryanlenk/lenkraster/main/docs/examples/hammer-cycle-fixed-palette.png)
84
+
85
+ <details>
86
+ <summary>Inspect the unnormalized source strip</summary>
87
+
88
+ ![The same four-frame hammer cycle before fixed-palette normalization](https://raw.githubusercontent.com/itsryanlenk/lenkraster/main/docs/examples/hammer-cycle-source.png)
89
+
90
+ </details>
91
+
92
+ ```console
93
+ lenkraster quantize docs/examples/hammer-cycle-source.png \
94
+ --palette-file docs/examples/hammer-cycle-palette.json \
95
+ --root <absolute-path-to-repository> \
96
+ --out hammer-cycle-local-output.png
97
+ ```
98
+
99
+ Use the same palette file and settings for separately stored frames. `cycle` measures
100
+ rendered-pixel motion and visibility; it does not claim to judge visual quality or prove
101
+ palette membership.
102
+
103
+ ### Aseprite export and cycle QA
104
+
105
+ The same four poses were assembled into a tagged `hammer` animation and exercised through
106
+ the separately installed, SHA-256-pinned Aseprite CLI bridge:
107
+
108
+ | Validated export | Advisory cycle report |
109
+ |---|---|
110
+ | 4 frames; 543x724 each; 120 ms per frame | `PASS`; 3/3 adjacent transitions detected; no issues |
111
+
112
+ LenkRaster exported the horizontal sheet and sanitized metadata into a new directory, then
113
+ ran the cycle check from a separate transient export. The `.aseprite` working document is
114
+ not distributed in the package or public repository.
115
+
116
+ ### Color construction
117
+
118
+ | Seven-stop OKLCH material ramp | Ordered 4x4 Bayer dither |
119
+ |---|---|
120
+ | ![Seven-stop orange material ramp with hue-shifted shadows and highlights](https://raw.githubusercontent.com/itsryanlenk/lenkraster/main/docs/examples/material-ramp.png) | ![Ordered blue and yellow Bayer dither](https://raw.githubusercontent.com/itsryanlenk/lenkraster/main/docs/examples/ordered-dither.png) |
121
+
122
+ ```console
123
+ lenkraster ramp --color "#FF6B35" --stops 7 --drift -10 \
124
+ --out docs/examples/material-ramp.png
125
+ lenkraster dither --a "#2196F3" --b "#FFEB3B" --size 256 --order 4 \
126
+ --out docs/examples/ordered-dither.png
127
+ ```
128
+
129
+ These examples demonstrate deterministic transformations, not artwork approval; critique
130
+ and animation QA remain advisory evidence for human review.
131
+
132
+ ## Security model
133
+
134
+ LenkRaster processes image files, so limits and containment are part of its public
135
+ contract:
136
+
137
+ - MCP file operations require an explicit, existing `LENKRASTER_TRUSTED_ROOT`.
138
+ - Resolved inputs and outputs must remain beneath that root; traversal and symlink escapes
139
+ fail closed.
140
+ - PNG encoded bytes, decoded pixels, dimensions, frame discovery, frame count, total
141
+ pixels, pair work, JSON size, and MCP request size are bounded.
142
+ - Quantized and Aseprite exports are create-only. Existing files and directories are never
143
+ overwritten.
144
+ - Public errors are fixed messages and do not include local paths or subprocess output.
145
+ - The server is trusted-local stdio only. It is not a network service and must not be
146
+ exposed as public HTTP.
147
+
148
+ The optional Aseprite bridge executes a native application and is not an operating-system
149
+ sandbox. Only process documents you trust. Use an OS sandbox for hostile or untrusted
150
+ Aseprite files.
151
+
152
+ See [SECURITY.md](https://github.com/itsryanlenk/lenkraster/blob/main/SECURITY.md) for
153
+ reporting and the maintained threat boundary.
154
+
155
+ ## Requirements
156
+
157
+ - Python 3.10 or newer
158
+ - NumPy 2.x
159
+ - Pillow 12.3 or newer within the 12.x line
160
+ - Optional: Aseprite 1.3.17.2 or newer in the 1.3 line for `.ase`/`.aseprite`
161
+ integration
162
+
163
+ Aseprite is not bundled, downloaded, or redistributed by LenkRaster. It is a separate
164
+ product with its own license. LenkRaster is compatible with its documented CLI and is not
165
+ affiliated with or endorsed by Aseprite.
166
+
167
+ ## Install
168
+
169
+ From PyPI:
170
+
171
+ ```console
172
+ python -m pip install lenkraster
173
+ ```
174
+
175
+ The distribution and installed Python package are both `lenkraster`. The command-line
176
+ entry points are `lenkraster` and `lenkraster-mcp`.
177
+
178
+ From a checked-out source tree:
179
+
180
+ ```console
181
+ python -m venv .venv
182
+ .venv/Scripts/python -m pip install --upgrade pip
183
+ .venv/Scripts/python -m pip install .
184
+ ```
185
+
186
+ On macOS or Linux, use `.venv/bin/python` in place of
187
+ `.venv/Scripts/python`.
188
+
189
+ For development:
190
+
191
+ ```console
192
+ python -m pip install -c requirements/ci.txt -e ".[dev]"
193
+ python -m pytest tests -q
194
+ python -m pip check
195
+ python -m pip_audit --skip-editable
196
+ ```
197
+
198
+ ## CLI
199
+
200
+ ```console
201
+ lenkraster critique sprite.png
202
+ lenkraster critique sheet.png --json report.json
203
+ lenkraster ramp --color "#d77643" --stops 5 --drift -8 --out swatches.png
204
+ lenkraster dither --a "#ac530b" --b "#ffe0cc" --order 4 --out gradient.png
205
+ lenkraster check --file sprite.png
206
+ lenkraster quantize frame.png --palette lenk-studio-32 --out snapped.png
207
+ lenkraster quantize frame.png --palette-file palettes/my-palette.json \
208
+ --root <absolute-path-to-sprite-workspace> --out snapped-custom.png
209
+ lenkraster palettes
210
+ lenkraster cycle "frame-*.png"
211
+ lenkraster shadow corpus/manifest.json --manifest-sha256 <sha256>
212
+ ```
213
+
214
+ `cycle` returns exit code 0 for `PASS` and 1 for `REVIEW`. A review result is
215
+ not a release rejection unless your project has separately adopted that policy.
216
+
217
+ ### User-owned palette files
218
+
219
+ `lenkraster palettes` lists the four original built-ins: `lenk-cinder-16`, `lenk-fern-4`,
220
+ `lenk-signal-16`, and `lenk-studio-32`. A user-owned palette is a UTF-8 JSON file below an
221
+ explicit trusted root:
222
+
223
+ | Built-in | Colors | Intended use |
224
+ |---|---:|---|
225
+ | `lenk-fern-4` | 4 | Compact moss-and-ink monochrome work |
226
+ | `lenk-cinder-16` | 16 | Warm industrial sprites and environment tiles |
227
+ | `lenk-signal-16` | 16 | High-contrast characters and UI accents |
228
+ | `lenk-studio-32` | 32 | General-purpose multi-material sprite work |
229
+
230
+ ```json
231
+ {
232
+ "name": "My fixed palette",
233
+ "author": "Local user",
234
+ "colors": ["102030", "f0e0d0"]
235
+ }
236
+ ```
237
+
238
+ Palette files are limited to 16 KiB, 2-64 unique six-digit RGB colors, exact keys, and
239
+ bounded path-free metadata. MCP callers use `palette_file` relative to
240
+ `LENKRASTER_TRUSTED_ROOT`; CLI callers use `--palette-file` with `--root`.
241
+
242
+ Common 4-, 16-, and 32-color external palette workflows were exercised during
243
+ pre-publication compatibility testing. Users may place any lawfully obtained palette in
244
+ their own local JSON file and use the same `palette_file` interface. Users remain
245
+ responsible for the provenance and permitted use of supplied palettes.
246
+
247
+ ### Optional Aseprite bridge
248
+
249
+ Set the executable to an absolute path:
250
+
251
+ ```console
252
+ LENKRASTER_ASEPRITE_EXECUTABLE=<absolute-path-to-aseprite>
253
+ LENKRASTER_ASEPRITE_SHA256=<sha256-of-that-executable>
254
+ ```
255
+
256
+ The SHA-256 pin is strongly recommended. LenkRaster checks it before the version probe
257
+ and again immediately before export, and rejects unsupported Aseprite versions. Recompute
258
+ the pin whenever Aseprite is intentionally updated.
259
+
260
+ Export a horizontal sheet and sanitized manifest into a directory that does not yet exist:
261
+
262
+ ```console
263
+ lenkraster aseprite-export hero.aseprite \
264
+ --root <absolute-path-to-sprite-workspace> \
265
+ --out-dir exports/hero \
266
+ --tag walk
267
+ ```
268
+
269
+ Run cycle QA without retaining exported files:
270
+
271
+ ```console
272
+ lenkraster aseprite-cycle hero.aseprite \
273
+ --root <absolute-path-to-sprite-workspace> \
274
+ --tag walk \
275
+ --motion-threshold 12 \
276
+ --min-motion-pixels 6
277
+ ```
278
+
279
+ The bridge passes a fixed argument vector to Aseprite batch mode. Callers cannot supply
280
+ scripts, arbitrary flags, output filenames, or shell fragments. Tag and layer selections
281
+ are optional bounded labels; nested layers use `/` (for example,
282
+ `characters/hero body`) and Unicode names are supported. Each invocation uses a fresh
283
+ disposable Aseprite user/configuration folder and a snapshotted input document, so the
284
+ operator's normal Aseprite extensions and preferences are not loaded. Validated output is
285
+ written to a hidden staging directory and becomes visible with one final create-only
286
+ directory rename.
287
+
288
+ ## MCP server
289
+
290
+ Any MCP-speaking client can launch the installed one-shot stdio server. Always use
291
+ absolute paths in the client configuration:
292
+
293
+ ```json
294
+ {
295
+ "mcpServers": {
296
+ "lenkraster": {
297
+ "command": "<absolute-path-to-venv>/Scripts/lenkraster-mcp.exe",
298
+ "args": [],
299
+ "env": {
300
+ "LENKRASTER_TRUSTED_ROOT": "<absolute-path-to-disposable-sprite-workspace>",
301
+ "LENKRASTER_ASEPRITE_EXECUTABLE": "<absolute-path-to-aseprite>",
302
+ "LENKRASTER_ASEPRITE_SHA256": "<sha256-of-that-executable>"
303
+ }
304
+ }
305
+ }
306
+ }
307
+ ```
308
+
309
+ The Aseprite variables are optional when those tools are not used. The trusted-root
310
+ variable is required for every tool call. MCP clients cannot override the configured
311
+ executable or its SHA-256 pin.
312
+
313
+ LenkRaster exposes seven tools:
314
+
315
+ | Tool | Purpose |
316
+ |---|---|
317
+ | `critique_sprite` | Score one bounded PNG and return findings/retry hints. |
318
+ | `make_ramp` | Generate a bounded OKLCH material ramp. |
319
+ | `palette_quantize` | Create a new PNG using an original built-in or trusted user palette. |
320
+ | `contrast_report` | Report same-hue lightness separation. |
321
+ | `qa_cycle` | Check 2-32 same-size PNG frames matched by a relative glob. |
322
+ | `aseprite_export` | Create a validated sheet and path-free manifest. |
323
+ | `qa_aseprite_cycle` | Export transiently and run bounded cycle QA. |
324
+
325
+ Project-specific adapters and private art corpora are intentionally outside the MCP
326
+ surface and the distribution.
327
+
328
+ ## Python API
329
+
330
+ ```python
331
+ from lenkraster import (
332
+ critique,
333
+ export_aseprite_document,
334
+ load_palette_file,
335
+ make_ramp,
336
+ qa_aseprite_document,
337
+ qa_cycle,
338
+ quantize_file,
339
+ )
340
+
341
+ report = critique("sprite.png")
342
+ print(report["score"], report["retry_hints"])
343
+
344
+ ramp = make_ramp("#d77643", stops=5, drift=-8.0)
345
+ quantize_file("frame.png", "lenk-studio-32", "snapped.png")
346
+ load_palette_file("palettes/my-palette.json", trusted_root="sprite-workspace")
347
+ quantize_file(
348
+ "frame.png",
349
+ None,
350
+ "snapped-custom.png",
351
+ palette_file="palettes/my-palette.json",
352
+ palette_root="sprite-workspace",
353
+ )
354
+
355
+ motion = qa_cycle(
356
+ ["base.png", "frame-1.png", "frame-2.png"],
357
+ roi=(12, 8, 44, 40),
358
+ transition_groups={"walk": [(0, 1), (1, 2)]},
359
+ )
360
+
361
+ export_manifest = export_aseprite_document(
362
+ "hero.aseprite",
363
+ "exports/hero",
364
+ trusted_root="sprite-workspace",
365
+ )
366
+
367
+ aseprite_motion = qa_aseprite_document(
368
+ "hero.aseprite",
369
+ trusted_root="sprite-workspace",
370
+ tag="walk",
371
+ )
372
+ ```
373
+
374
+ For the Aseprite Python API, configure `LENKRASTER_ASEPRITE_EXECUTABLE` or pass an
375
+ absolute executable path through the trusted local API. MCP clients cannot choose the
376
+ executable.
377
+
378
+ ## Design laws
379
+
380
+ - **Rendered-pixel law:** RGB beneath fully transparent pixels does not affect critique,
381
+ contrast, quantization, or motion.
382
+ - **Shared-palette law:** animation frames should use the same fixed target palette and
383
+ settings.
384
+ - **Motion-group law:** every authored gesture group must independently contain a
385
+ qualifying transition.
386
+ - **Vision-is-not-QA law:** animation correctness is computed from pixels, not inferred
387
+ from thumbnails.
388
+ - **Bounded-work law:** encoded bytes, decoded pixels, frames, discovery, and comparisons
389
+ fail closed at explicit ceilings.
390
+ - **Advisory law:** LenkRaster produces evidence and retry hints, never art approval.
391
+
392
+ ## Golden-corpus regression
393
+
394
+ `lenkraster shadow` accepts a bounded, caller-hash-pinned manifest containing
395
+ user-owned PNG fixtures. Automated cases report `BASELINE_MATCH` or `DRIFT`;
396
+ contextual cases remain `HUMAN_REVIEW`. Do not commit proprietary or private artwork to
397
+ this public repository.
398
+
399
+ ## Development and release
400
+
401
+ Contributions are welcome; read
402
+ [CONTRIBUTING.md](https://github.com/itsryanlenk/lenkraster/blob/main/CONTRIBUTING.md).
403
+ Public artifact checks
404
+ and release provenance steps are documented in
405
+ [docs/release-checklist.md](https://github.com/itsryanlenk/lenkraster/blob/main/docs/release-checklist.md).
406
+
407
+ The source and repository-authored assets are MIT licensed. See
408
+ [ASSET_LICENSE.md](https://github.com/itsryanlenk/lenkraster/blob/main/ASSET_LICENSE.md)
409
+ for palette and image scope, and
410
+ [THIRD_PARTY_NOTICES.md](https://github.com/itsryanlenk/lenkraster/blob/main/THIRD_PARTY_NOTICES.md)
411
+ for dependency, method, and optional-tool notices. User-supplied palettes and Aseprite
412
+ itself are not part of this distribution.