Changed
- Transparent inputs flatten before decode (
pipeline1→2,
score_contract3→4) — the engine used to read the RGB stored under
the transparency (alpha was dropped at the input seam), which made the
verdict exporter-dependent: the same visual design scored 100
(white-under-alpha exports), 74 (canvas-style black-under-alpha) or
"no detection" (dark modules over canvas transparency) depending on
which tool wrote the PNG. Since this release the input composites over
a DECLARED background before luma conversion and RGB extraction —
every stage (ladder · score · UEC · ISO) sees the flattened image, and
the stored under-alpha RGB can never influence the report
(exporter-invariance is pinned by test).auto(the default) measures
the design on its intended placement: dark content over white, light
content over black, with an opposite-background retry on a
zero-detection scan (alpha.fallback_used) so a mis-called mean can
never produce a false "no detection". Opaque inputs are untouched —
their reports stay byte-identical (thealphakey is omitted
entirely, notnull). Spec: 01-report § Alpha · 07-pipeline.
Added
-
ScanConfig.alpha_backgroundon every surface, per the
score_skip_axesprecedent — core (AlphaBackground+
from_name), wasm (scan_image(..., alpha_background)), Node
(alphaBackgroundoption, typed
"auto" | "white" | "black" | "none" |hex), CLI
(--alpha-background), Python (alpha_background=kwarg, stub
updated), UniFFI/Kotlin/Swift (alpha_backgroundarg with a typed
UnknownAlphaBackgrounderror).#rrggbbforces the host's real
placement surface — the truest verdict when the embedder knows where
the code will sit;nonerestores the drop-the-channel behavior as
an escape hatch. Unknown values fail loud on every surface. (The
Flutter facade parses the new block; its option arg lands with the
facade's next options pass — it exposes neither budget nor skips
today.) -
The placement envelope (
alpha.envelope, Full profile) — the
transparent-design question is not "does it scan" but "over which
backgrounds does it keep scanning": five fixed neutral rungs
(luma 0 · 64 · 128 · 192 · 255) plus one bisection probe per verdict
boundary, each a quick decode in the primary symbol's own decode
class. The report carries the probes (every endpoint a TESTED
background, never interpolation), the contiguoussafe_lumabands,
and aplacementverdict (any·light_only·dark_only·
mixed·none). Informational by contract: it never moves
score.value. Skippable through the standard seam
(score_skip_checks: ["alpha_envelope"]—ScoreCheckgains the
variant on every surface). -
Hint::AlphaBackgroundDependent { placement }— fires when the
envelope is narrower thanany: pin a background layer in the
artwork, or constrain placement to the safe bands. The generator/UI
loop's alpha feedback. -
report.alphablock:coverage·content_luma·mode·
background("white"/"black"/"#rrggbb") ·fallback_used
·envelope. Schema, TS types, Dart mirror and the type-parity gate
updated in lockstep;qrscan --prettyrenders the flatten verdict
and the envelope strip. -
alpha_paletteon every option surface (alphaPalette/
--alpha-palette) — the host's theme/brand colors probed by the
envelope inside the SAME scan: per-color verdicts land in
alpha.envelope.palette(request order, each at its BT.601 luma),
instead of one full scan per candidate background. Palette colors
never distort the neutral probes/bands/placement. Unknown colors fail
loud (white·black·#rrggbbonly — the flatten keywords are
modes, not colors). -
Hint::AddBackgroundPlate { color }— the canonical REMEDY riding
with thealpha_background_dependentdiagnosis: add an opaque plate
(rounded rectangle + quiet-zone margin) behind the symbol;color
(whitefor dark content,blackfor light) maximizes contrast.
Maps 1:1 to an editor's one-click fix. -
score.weights_run— the honesty integer: Σ contract weights of the
axes that ran (100 = the full six-axis contract). A partial score now
SAYS how much contract stands behind it; pre-0.9 reports parse
leniently (default 0).qrscan --prettyrenders "· 70% of contract"
when partial. -
score_preseton every option surface (scorePreset/
--score-preset): the two canonical postures by NAME —design
(generated previews: skips perspective+rotation, keeps lighting) ·
capture(all six). Sugar overscore_skip_axes, mutually exclusive
with an explicit list (rejects loudly on every surface) — N host
integrations can no longer drift apart on hand-built lists. -
axes[].refined_failed_at— the knee, bisected once at Full depth:
the tightest TESTED failing intensity ("blur 2.25", "435px", "63%").
One extra probe per kneed ramp; the composite never reads it; absent
when no refinement ran. The generate→scan→regenerate loop sees
sub-cell progress instead of 2-4 point cliffs.