artgate: add 64px icon spec for the diffusion route
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Python tests / test (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run

The diffusion route generates at SDXL's native 1024px, so a 32px icon is a
32x downscale. Detail that survives the generator does not survive that
reduction -- a coin's struck emblem, a shield's radial pattern and a
lantern's glass panes all mush at 32px and read clearly at 64px.

max_colors is deliberately NOT scaled with the canvas. Measured over the 300
genuine 48x48 icons in ~/opengameart-downloads: p50=9 p90=13 max=14. A cap of
32 rejects 0.00% of that art and so does every larger cap, so hand-made art
does not spend more colours on a bigger canvas. Raising it would only admit
more un-quantized generator output.

Gates 18/18: the 12 original bake-off subjects re-finished at 64px, plus six
subjects the route had never seen (lantern, axe, boots, helmet, torch,
mushroom).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9rQ12yZ7sJxiPQTCS5dUF
This commit is contained in:
Krzysztof Rudnicki 2026-08-15 23:30:32 +02:00
parent a0db432933
commit 95a1f62fc4
2 changed files with 76 additions and 0 deletions

View File

@ -0,0 +1,42 @@
{
"intent": "Add a 64x64 icon spec so the diffusion route (the bake-off winner) has a deterministic gate at the size where its detail actually survives. At 32px a coin's struck emblem, a shield's radial pattern and a lantern's glass panes mush; the same source reads clearly at 64px.",
"scope": [
"python_pkg/artgate/styles/pixel_icon_64.toml (new file)",
"Non-goal: pixel_icon.toml (32px) is unchanged and remains the default spec",
"Non-goal: no code changes; this is a style spec consumed by the existing CLI"
],
"changes": [
"New style spec: canvas 64x64, min_margin 2 (keeps 32px's 3.1% proportional margin rather than tightening it), same gates in the same order as pixel_icon.",
"max_colors deliberately held at 32 rather than scaled with the canvas. Calibrated against the 300 genuine 48x48 icons in ~/opengameart-downloads (the largest real-art sample above 32px available locally): p50=9 p75=11 p90=13 p95=13 p99=14 max=14. A cap of 32 rejects 0.00% of that art, and so does every larger cap -- hand-made art does not spend more colours on a bigger canvas, so raising the cap would only admit more un-quantized generator output."
],
"verification": [
{
"command": "python -m python_pkg.artgate.cli <icon> --spec python_pkg/artgate/styles/pixel_icon_64.toml --json",
"result": "pass",
"evidence": "18/18. The 12 original bake-off subjects re-finished at 64px: 12/12 exit 0. Six brand-new subjects the route had never seen (lantern, axe, boots, helmet, torch, mushroom): 6/6 exit 0."
},
{
"command": "corpus calibration over ~/opengameart-downloads 48x48 icons",
"result": "pass",
"evidence": "n=300, max observed colour count 14, so max_colors=32 admits 100% of real art. Recorded so the cap is traceable to measurement rather than to a number that made one file pass."
},
{
"command": "end-to-end default run: generate a new subject with no flags, then gate it",
"result": "pass",
"evidence": "~/artgate-gen/generate.py with defaults produced a 64px anvil; CLI returned {\"codes\": [], \"exit\": 0}. This was a real regression check -- the generator originally derived the palette cap from the canvas size and produced 64-colour icons that failed TOO_MANY_COLORS on all 18 subjects."
},
{
"command": "pre-commit run --files python_pkg/artgate/styles/pixel_icon_64.toml docs/superpowers/evidence/artgate-pixel-icon-64-spec-2026-08-15.json",
"result": "pass",
"evidence": "Every hook reported Passed or Skipped (no files to check); none Failed."
}
],
"risks": [
"No 64x64 corpus exists locally, so the colour cap is extrapolated from 48x48 art. The extrapolation is conservative (holding the cap rather than raising it) and the 48px data trends downward in colour count as canvas grows, so the risk is admitting too little, not too much.",
"64px icons are 4x the pixel area of 32px. Anything consuming these needs to opt in by naming the spec; nothing switches size implicitly."
],
"rollback": [
"Delete pixel_icon_64.toml. Nothing imports it -- it is selected by --spec at call time, so removal cannot break the 32px path.",
"After rollback, confirm the 32px path still gates: run the CLI against a 32px icon with pixel_icon.toml and expect exit 0."
]
}

View File

@ -0,0 +1,34 @@
# Pixel-art item icon at 64x64: same style as `pixel_icon`, larger canvas.
#
# WHY THIS EXISTS. The diffusion route generates at SDXL's native 1024px, so a
# 32px icon is a 32x downscale. Fine detail that survives the generator does not
# survive that reduction: a coin's struck emblem, a shield's radial pattern and
# a lantern's glass panes all mush at 32px and read clearly at 64px.
#
# CALIBRATION. `max_colors` is deliberately NOT raised from pixel_icon's 32.
# Measured over the 300 genuine 48x48 icons in ~/opengameart-downloads -- the
# largest real-art sample available locally above 32px:
# p50=9 p75=11 p90=13 p95=13 p99=14 max=14
# Rejection rate of that real art: cap 32 -> 0.00%, and every larger cap also
# 0.00%. Hand-made art does not spend more colours just because the canvas grew,
# so raising the cap would only admit more un-quantized generator output. The
# cap stays where the corpus puts it.
#
# min_margin scales with the canvas: 1px of 32 is 3.1%, so 2px of 64 keeps the
# same proportional breathing room rather than tightening it.
name = "pixel_icon_64"
canvas = [64, 64]
max_colors = 32
alpha_binary = true
min_margin = 2
# Same gates and same ordering as pixel_icon -- see that file for why
# ALPHA_NOT_BINARY must come first.
gates = [
"ALPHA_NOT_BINARY",
"TOO_MANY_COLORS",
"CANVAS_SIZE",
"SCALE_NOT_INVARIANT",
"SILHOUETTE_LOW_CONTRAST",
"MARGIN_TOO_SMALL",
]