RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/imazen-imageflow-cursor-rules-ffi ↔ imazen-imageflow-claude

Comparison

A · Cursor rules · imazen/imageflowB · CLAUDE.md · imazen/imageflow
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections00140%
Commands0070%
Section tags0060%

What each file covers

Sections

0 shared · 0 only in A · 14 only in B
  • + Imageflow Project Instructions
  • + SIMD & Dispatch Crates
  • + f32/f64 Clamping
  • + Git Workflow
  • + Test Commands
  • + Known Bugs
  • + FIXED (2026-07-19) — generic-quality double-mapping: ssim2 score was fed into the libjpeg-turbo-quality knob (WebP/JXL/AVIF)
  • + FIXED (2026-06-25) — animated WebP/AVIF/GIF decode+encode were uncancellable (`None` at frame sites)
  • + FIXED (2026-06-25) — `byte_ceiling` promoted the soft avg pre-flight threshold into a hard runtime cap
  • + LOW / not-a-clear-bug — `High`-profile JPEG `gq > 85.0` chroma boundary (ZEN-ONLY build)
  • + MEDIUM — single-frame PNG/WebP/AVIF/JXL decode ignores `max_threads`
  • + LOW / interim (2026-06-24 audit)
  • + Audit Notes (2026-06-22, "since v2.3.1-rc01" review)
  • + Delayed TODOs

Commands

0 shared · 0 only in A · 7 only in B
  • + just test
  • + just test-filter NAME
  • + just test-update
  • + just test-replace
  • + just test-list
  • + just test-build
  • + cargo fmt

Section tags

0 shared · 0 only in A · 6 only in B
  • + build
  • + test
  • + code-style
  • + git-pr
  • + do-not
  • + agent-behaviour

Line diff

+154 added−8 removed5 unchanged3.1% identical
imazen/imageflow · .cursor/rules/ffi.mdc
@@ −1 @@
1---
2description:
3globs:
4alwaysApply: true
5---
6Imageflow offers a very stable ABI, focused on sending/recieving JSON and byte buffers in conjuction with an endpoint string. There are wrappers in C# (the main one), go, node, elixr, and more. Keeping these wrappers up-to-date with the latest JSON features is difficult, and the nature of the JSON graph structure makes it require simpler API wrappers.
7 
8For the HTTP-like endpoint names, see @/imageflow_core/src/context_method.rs
9 
10For the FFI ABI, read @/imageflow_abi/src/lib.rs and [imageflow_default.h](mdc:bindings/headers/imageflow_default.h)
11 
 
12 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13 
imazen/imageflow · CLAUDE.md
@@ +1 @@
1# Imageflow Project Instructions
 
 
 
 
 
2 
3## SIMD & Dispatch Crates
4 
5`multiversion` is allowed in this project for autovectorization dispatch on scalar loops (e.g., `scaling.rs`). Prefer the defaults provided by `multiversed` for new code — use `multiversion` only where `multiversed` doesn't fit.
6 
7For explicit SIMD intrinsics, use `archmage` (already in use for `transpose.rs`).
8 
9## f32/f64 Clamping
10 
11**Do NOT replace `min(max(...))` patterns with `.clamp()` on floats.** `f32::clamp()` propagates NaN, while `min(max(...))` suppresses it. In image processing pipelines, NaN propagation turns a single bad pixel into a full-image corruption. The `min(max(...))` pattern is intentional NaN defense.
12 
13## Git Workflow
14 
15Always commit `cargo fmt` changes as a separate commit from code changes.
16 
17## Test Commands
18 
19All integration tests live in `imageflow_core/tests/integration/` as a single binary.
20 
21```bash
22just test # run all tests with nextest
23just test-filter NAME # run tests matching NAME
24just test-update # run tests, auto-accept checksums within tolerance
25just test-replace # reset all checksum baselines to current output
26just test-list # list all test names
27just test-build # compile-check tests without running
28```
29 
30Checksum baselines: `imageflow_core/tests/integration/visuals/*.checksums` (one line-format file
31per suite: canvas, codec, color, composition, icc, idct, orientation, scaling, trim, watermark)
32Reference images: `imageflow_core/tests/integration/visuals/images/<suite>/`
33 
34## Known Bugs
35 
36### FIXED (2026-07-19) — generic-quality double-mapping: ssim2 score was fed into the libjpeg-turbo-quality knob (WebP/JXL/AVIF)
37*Found & source-verified 2026-06-24; re-verified live and FIXED 2026-07-19.* `auto.rs` passed
38`generic_quality_ssim2(qp)` — an SSIMULACRA2 **score** — into `ZenEncoder::create_{webp,jxl,avif}`, which
39forward it to zencodec `with_generic_quality(...)`. That trait knob is a **calibrated 0–100 libjpeg-turbo
40quality** (`zencodec 0.1.24 traits/encoding.rs:54`), which each codec re-maps via its OWN calibration
41(zenwebp `calibrated_webp_quality`, zenavif `calibrated_avif_quality`, jxl-encoder `calibrated_jxl_quality`
42— all documented "Map generic quality (libjpeg-turbo scale) to … native quality"). Quality was mapped TWICE
43through mismatched units: `High` deflated (intended webp native ≈91.3, delivered ≈85.9); low-q profiles
44inflated — worst in the q5–q40 web range. **Fixed:** the three knob sites now pass the profile's
45libjpeg-turbo quality via `zencodec_generic_quality` (= `approximate_quality_profile`);
46`generic_quality_ssim2` is reserved for the zen JPEG path (`Quality::ApproxSsim2`, `zen_encoder.rs:88`),
47which genuinely takes ssim2 units. Unit tests `quality_mapping_tests::*` (auto.rs) pin both mappings
48(red-verified against the buggy mapping first: got 83.2 for `High`, expected 91.0). Encoded output changes
49for quality_profile-driven WebP (zen-only build) and JXL/AVIF (zen-codecs builds) — intended; no in-tree
50checksum baselines cover those paths (default CI build is c-codecs, where Jxl/Avif auto-select is disabled
51via `FEATURES_IMPLEMENTED` and the zen WebP/JPEG arms don't compile).
52 
53### FIXED (2026-06-25) — animated WebP/AVIF/GIF decode+encode were uncancellable (`None` at frame sites)
54*Found 2026-06-24, fixed 2026-06-25.* The animation frame loop passed `None` for the per-call stop at all four
55sites (`render_next_frame_owned`, `push_frame`, `finish`); zengif/zenwebp/zenavif drop the job stop and honor
56only that per-call arg, so animated WebP/AVIF/GIF couldn't be interrupted mid-flight (animated JXL was fine —
57zenjxl carries the job stop). **Fixed:** the four sites now thread `Some(&stop as &dyn Stop)` — from
58`c.cancellation_token()` on the two decode sites and the push site, and from a persisted `stop_token` field on
59the encoder for `finish()` (which runs in `into_io`, where there is no `Context`). Verified: 20 animation +
6010 gif-limit + 3 webp integration tests pass. (A deterministic *mid-frame* cancellation test stays impractical
61without a codec pause-hook; the pre-call `return_if_cancelled!` gate already covers between-frame cancellation.)
62 
63### FIXED (2026-06-25) — `byte_ceiling` promoted the soft avg pre-flight threshold into a hard runtime cap
64*Found 2026-06-24, fixed 2026-06-25.* `MemBudgetPolicy::byte_ceiling()` min'd over ALL three thresholds
65including `require_est_bytes_below` — a soft pre-flight check on the **avg** estimate (`check_estimates` →
66`peak_avg`) — and that ceiling became the codec's hard `max_memory_bytes` cap, so a caller who set only the
67advisory avg threshold got a hard `B − buffer` cap and a mid-flight OOM-reject despite passing pre-flight.
68**Fixed:** `byte_ceiling()` now min's over only the conservative thresholds (`require_est_max_bytes_below`,
69`require_tracked_bytes_below`). Regression tests `byte_ceiling_excludes_soft_avg_threshold` +
70`check_estimates_gates_on_the_right_metric` added (imageflow_types). Was mostly latent (all codec estimates 0).
71 
72### LOW / not-a-clear-bug — `High`-profile JPEG `gq > 85.0` chroma boundary (ZEN-ONLY build)
73*Re-traced 2026-06-25 — the 2026-06-24 "MEDIUM regression / default High JPEG" framing was WRONG.* Facts that
74hold: zen `create_jpeg` sets `full_chroma = gq > 85.0`; `High` → `generic_quality_ssim2 = 85.0` exactly (the
75`(91.0, 85.0)` table knot), so `85.0 > 85.0` is false → 4:2:0. BUT: (1) this fires ONLY in the **zen-only
76build** — the `auto.rs` JPEG arm routes the generic-quality path under `#[cfg(all(not(c-codecs), zen-codecs))]`;
77the default `c-codecs` build encodes JPEG via the C `MozjpegEncoder`, and the runtime-picked `ZenJpegEncoder`
78path (`auto.rs:143`) passes `generic_quality = None` → uses the `q > 90` branch, not this boundary. (2) The C
79path's chroma is decided **content-adaptively** by `evalchroma::adjust_sampling(buf, {2,2}, chroma_quality)`
80per image — a fixed `gq` threshold cannot match it regardless of `>` vs `>=`. So this is NOT a default-build
81regression and NOT a clear bug; it's a heuristic-threshold calibration question confined to the zen-only JPEG
82path. `gq >= 85.0` would flip High → 4:4:4 there, but whether that better approximates evalchroma is
83content-dependent — needs corpus measurement, not a blind 1-char change.
84 
85### MEDIUM — single-frame PNG/WebP/AVIF/JXL decode ignores `max_threads`
86*Source-verified 2026-06-24.* Only JPEG takes the buffered `run_pooled` path; PNG/WebP/AVIF/JXL single-frame
87decode runs `job.push_decode(...)` on the ambient global rayon pool (`zen_decoder.rs:~932`), so
88`ExecutionSecurity.max_threads` is silently violated for exactly the most-parallel decoders (rav1d/jxl-rs).
89**Fix:** run the single-frame `push_decode` inside `install_pooled(&self.thread_pool, …)` (mind the `&mut`
90bitmap-window sink — the closure must own/move it).
91 
92### LOW / interim (2026-06-24 audit)
93- `v1/estimate` ignores `data.format` (`v1.rs:301` `let _ = &data.format;`) — encode side always 0, so the
94 returned `EncodeEstimate` is decode-only. Part of the #728 seam (codecs return `ResourceEstimate::unknown()`).
95- `check_estimates` `peak_max = …unwrap_or(peak_avg)` collapses the conservative gate onto the avg when a
96 codec sets `est` but not `max` (`ResourceEstimate::new` does exactly that). Apply a conservatism factor or
97 have codecs always set `max`.
98- `LIBJPEG_TURBO_Q_TO_SSIM2` (`auto.rs:649`) duplicates the `QUALITY_HINTS.ssim2` column verbatim — second
99 source of truth that will drift; generate one from the other.
100- Per-decode eager rayon pool (`zen_decoder.rs:239`) built even for single-frame decodes that never use it;
101 one-shot encode rebuilds a pool per call (`zen_encoder.rs:672`). Build lazily / reuse `self.thread_pool`.
102- Zero tests for the budgeting/estimate math (`check_estimates`/`byte_ceiling`/`v1/estimate`) and zero for
103 mid-flight cancellation. The `>=` reject boundary and the cap interaction are untested.
104 
105## Audit Notes (2026-06-22, "since v2.3.1-rc01" review)
106 
107- **CHANGELOG.md:13 is inaccurate**: claims `ExecutionSecurity` "gains … process timeout, and
108 cooperative cancellation handles." The actual struct (`imageflow_types/src/lib.rs:1127-1144`) has
109 ONLY size/byte/pixel limits — no timeout field, no cancellation handle. `JobOptions`
110 (`lib.rs:1476`) is an empty `#[non_exhaustive]` placeholder. No process timeout exists anywhere.
111- **CHANGELOG AVIF/HEIC drift**: AVIF decoder+encoder and BMP/PNM (`zenavif`/`zenbitmaps`) already
112 SHIPPED in `1bb00db5` but are listed under "QUEUED BREAKING CHANGES" as if pending; **HEIC is
113 genuinely absent** (no `heic` refs in `src/`). `ZenJxlDecoder` enum variant exists with no `zenjxl`
114 dep (scaffolding). `f06b478b` (moxcms widen) is uncited; `8e6f2483`→`bd545d11` IDCT churn is invisible.
115- **Privacy metadata = stripped by construction** (clean result): no encoder writes EXIF/XMP/IPTC/GPS;
116 EXIF orientation is applied-to-pixels-then-dropped; no preserve-metadata option exists. Source ICC is
117 read for the color transform but never re-embedded — output is plain sRGB-by-convention (only the C
118 libpng path writes an sRGB marker, `codec_png_wrapper.c:420`).
119- **Quality units**: only a codec-agnostic 0–100 scalar (`QualityProfile`, `lib.rs:644`) → static
120 per-codec tables (`auto.rs:557-716 QUALITY_HINTS`). No metric-target unit (zensim/ssim2/butteraugli);
121 `jxl.distance` is the only metric-flavored knob (JXL-only passthrough). The `ssim2` column in
122 `QUALITY_HINTS` is internal DPR-math only, not a request unit. `zensim` is a **dev-dependency only**.
123 `QualityIntent`/`codec_decisions.rs` (the richer design) is on branch `feat/zen-codecs-v3`, NOT HEAD,
124 and still collapses to one `generic_quality` float — would need structural change for metric targeting.
125 
126## Delayed TODOs
127 
128- **ssim2↔quality table calibration — JPEG dials DONE, remaining axes still guessed.**
129 - **DONE (2026-06-26):** `LIBJPEG_TURBO_Q_TO_SSIM2` in `imageflow_core/src/codecs/auto.rs` is now
130 **measured** (24 anchors), and a companion `MOZJPEG_EVALCHROMA_Q_TO_SSIM2` (24 anchors,
131 `#[allow(dead_code)]` until the JPEG path wires it) was added. Both are the median quality→SSIMULACRA2
132 curve from an 81,552-cell sweep (codec-corpus 502 images × {64,256,1024,native≤4MP} × q5–q100 × 2
133 encoders, fast-ssim2). Canonical copy + the `q_to_ssim2`/`ssim2_to_q`/`q_to_bpp` helpers + full
134 provenance live in `zencodecs::quality_calibration` (zenpipe); raw Parquet at
135 `/mnt/v/output/jpeg-q-ssim2-cal/2026-06-26/sweep.parquet`; docs + rosetta CSVs in
136 `imageflow/benchmarks/jpeg-q-ssim2-2026-06-26/`.
137 - **STILL UNCALIBRATED:** the `ssim2` column of `QUALITY_HINTS` (the DPR/quality-scalar math) has NO
138 empirical backing. The measured tables cover **JPEG dials only** — WebP/AVIF/JXL quality→ssim2 were
139 NOT swept, so the generic-quality target for those codecs is still a guess.
140 - **FIXED (2026-07-19) — the double-mapping correctness bug** (see the Known Bugs entry above):
141 auto.rs now feeds `with_generic_quality` the profile's *libjpeg-turbo 0–100 dial*
142 (`zencodec_generic_quality`); the ssim2-unit `generic_quality_ssim2` is reserved for the JPEG
143 `ApproxSsim2` path. Remaining follow-up: sweep WebP/AVIF/JXL to calibrate their own quality→ssim2
144 curves the way JPEG's dials now are (the codecs' internal libjpeg-q→native tables are CID22-512
145 medians; imageflow-side per-codec ssim2 columns in `QUALITY_HINTS` are still guesses, per the
146 bullet above).
147 
148- **Issue #728 zencodec passthrough (currently interim heuristic).** The `target=fast|optimal` +
149 balance directive and `is_optimal`/optimality-headroom annotations are implemented in imageflow with
150 an INTERIM local cost/RD heuristic (`auto.rs`), because zencodec 0.1.19 exposes no encode
151 resource-estimate, no candidate-`ImageFormat` selector, and no optimality API. zencodec 0.1.24 adds
152 `EncoderConfig::estimate_encode_resources` plumbing but every codec returns `ResourceEstimate::unknown()`.
153 Full passthrough needs: (1) imageflow on zencodec ≥ the release that ships real
154 `estimate_encode_resources` impls in zenavif/zenjpeg/zenwebp/zenjxl, and (2) NEW zencodec APIs (a
155 candidate-format selector + optimality/would-not-improve determination) that exist in no version yet.
156 Replace the interim seams (marked `// TODO(#728): zencodec passthrough`) when those land.
157 
158- **Licensing/caching module** (`imageflow_helpers/src/unused/`): ~2300 lines of draft licensing, caching, and polling code. Currently unreferenced (no `mod` declaration). Needs review, modernization, and wiring into the build when ready to complete.
159 
@@ −1 +1 @@
1−---
2−description:
3−globs:
4−alwaysApply: true
5−---
6−Imageflow offers a very stable ABI, focused on sending/recieving JSON and byte buffers in conjuction with an endpoint string. There are wrappers in C# (the main one), go, node, elixr, and more. Keeping these wrappers up-to-date with the latest JSON features is difficult, and the nature of the JSON graph structure makes it require simpler API wrappers.
1+# Imageflow Project Instructions
72  
8−For the HTTP-like endpoint names, see @/imageflow_core/src/context_method.rs
3+## SIMD & Dispatch Crates
94  
10−For the FFI ABI, read @/imageflow_abi/src/lib.rs and [imageflow_default.h](mdc:bindings/headers/imageflow_default.h)
5+`multiversion` is allowed in this project for autovectorization dispatch on scalar loops (e.g., `scaling.rs`). Prefer the defaults provided by `multiversed` for new code — use `multiversion` only where `multiversed` doesn't fit.
116  
7+For explicit SIMD intrinsics, use `archmage` (already in use for `transpose.rs`).
128  
9+## f32/f64 Clamping
10+ 
11+**Do NOT replace `min(max(...))` patterns with `.clamp()` on floats.** `f32::clamp()` propagates NaN, while `min(max(...))` suppresses it. In image processing pipelines, NaN propagation turns a single bad pixel into a full-image corruption. The `min(max(...))` pattern is intentional NaN defense.
12+ 
13+## Git Workflow
14+ 
15+Always commit `cargo fmt` changes as a separate commit from code changes.
16+ 
17+## Test Commands
18+ 
19+All integration tests live in `imageflow_core/tests/integration/` as a single binary.
20+ 
21+```bash
22+just test # run all tests with nextest
23+just test-filter NAME # run tests matching NAME
24+just test-update # run tests, auto-accept checksums within tolerance
25+just test-replace # reset all checksum baselines to current output
26+just test-list # list all test names
27+just test-build # compile-check tests without running
28+```
29+ 
30+Checksum baselines: `imageflow_core/tests/integration/visuals/*.checksums` (one line-format file
31+per suite: canvas, codec, color, composition, icc, idct, orientation, scaling, trim, watermark)
32+Reference images: `imageflow_core/tests/integration/visuals/images/<suite>/`
33+ 
34+## Known Bugs
35+ 
36+### FIXED (2026-07-19) — generic-quality double-mapping: ssim2 score was fed into the libjpeg-turbo-quality knob (WebP/JXL/AVIF)
37+*Found & source-verified 2026-06-24; re-verified live and FIXED 2026-07-19.* `auto.rs` passed
38+`generic_quality_ssim2(qp)` — an SSIMULACRA2 **score** — into `ZenEncoder::create_{webp,jxl,avif}`, which
39+forward it to zencodec `with_generic_quality(...)`. That trait knob is a **calibrated 0–100 libjpeg-turbo
40+quality** (`zencodec 0.1.24 traits/encoding.rs:54`), which each codec re-maps via its OWN calibration
41+(zenwebp `calibrated_webp_quality`, zenavif `calibrated_avif_quality`, jxl-encoder `calibrated_jxl_quality`
42+— all documented "Map generic quality (libjpeg-turbo scale) to … native quality"). Quality was mapped TWICE
43+through mismatched units: `High` deflated (intended webp native ≈91.3, delivered ≈85.9); low-q profiles
44+inflated — worst in the q5–q40 web range. **Fixed:** the three knob sites now pass the profile's
45+libjpeg-turbo quality via `zencodec_generic_quality` (= `approximate_quality_profile`);
46+`generic_quality_ssim2` is reserved for the zen JPEG path (`Quality::ApproxSsim2`, `zen_encoder.rs:88`),
47+which genuinely takes ssim2 units. Unit tests `quality_mapping_tests::*` (auto.rs) pin both mappings
48+(red-verified against the buggy mapping first: got 83.2 for `High`, expected 91.0). Encoded output changes
49+for quality_profile-driven WebP (zen-only build) and JXL/AVIF (zen-codecs builds) — intended; no in-tree
50+checksum baselines cover those paths (default CI build is c-codecs, where Jxl/Avif auto-select is disabled
51+via `FEATURES_IMPLEMENTED` and the zen WebP/JPEG arms don't compile).
52+ 
53+### FIXED (2026-06-25) — animated WebP/AVIF/GIF decode+encode were uncancellable (`None` at frame sites)
54+*Found 2026-06-24, fixed 2026-06-25.* The animation frame loop passed `None` for the per-call stop at all four
55+sites (`render_next_frame_owned`, `push_frame`, `finish`); zengif/zenwebp/zenavif drop the job stop and honor
56+only that per-call arg, so animated WebP/AVIF/GIF couldn't be interrupted mid-flight (animated JXL was fine —
57+zenjxl carries the job stop). **Fixed:** the four sites now thread `Some(&stop as &dyn Stop)` — from
58+`c.cancellation_token()` on the two decode sites and the push site, and from a persisted `stop_token` field on
59+the encoder for `finish()` (which runs in `into_io`, where there is no `Context`). Verified: 20 animation +
60+10 gif-limit + 3 webp integration tests pass. (A deterministic *mid-frame* cancellation test stays impractical
61+without a codec pause-hook; the pre-call `return_if_cancelled!` gate already covers between-frame cancellation.)
62+ 
63+### FIXED (2026-06-25) — `byte_ceiling` promoted the soft avg pre-flight threshold into a hard runtime cap
64+*Found 2026-06-24, fixed 2026-06-25.* `MemBudgetPolicy::byte_ceiling()` min'd over ALL three thresholds
65+including `require_est_bytes_below` — a soft pre-flight check on the **avg** estimate (`check_estimates` →
66+`peak_avg`) — and that ceiling became the codec's hard `max_memory_bytes` cap, so a caller who set only the
67+advisory avg threshold got a hard `B − buffer` cap and a mid-flight OOM-reject despite passing pre-flight.
68+**Fixed:** `byte_ceiling()` now min's over only the conservative thresholds (`require_est_max_bytes_below`,
69+`require_tracked_bytes_below`). Regression tests `byte_ceiling_excludes_soft_avg_threshold` +
70+`check_estimates_gates_on_the_right_metric` added (imageflow_types). Was mostly latent (all codec estimates 0).
71+ 
72+### LOW / not-a-clear-bug — `High`-profile JPEG `gq > 85.0` chroma boundary (ZEN-ONLY build)
73+*Re-traced 2026-06-25 — the 2026-06-24 "MEDIUM regression / default High JPEG" framing was WRONG.* Facts that
74+hold: zen `create_jpeg` sets `full_chroma = gq > 85.0`; `High` → `generic_quality_ssim2 = 85.0` exactly (the
75+`(91.0, 85.0)` table knot), so `85.0 > 85.0` is false → 4:2:0. BUT: (1) this fires ONLY in the **zen-only
76+build** — the `auto.rs` JPEG arm routes the generic-quality path under `#[cfg(all(not(c-codecs), zen-codecs))]`;
77+the default `c-codecs` build encodes JPEG via the C `MozjpegEncoder`, and the runtime-picked `ZenJpegEncoder`
78+path (`auto.rs:143`) passes `generic_quality = None` → uses the `q > 90` branch, not this boundary. (2) The C
79+path's chroma is decided **content-adaptively** by `evalchroma::adjust_sampling(buf, {2,2}, chroma_quality)`
80+per image — a fixed `gq` threshold cannot match it regardless of `>` vs `>=`. So this is NOT a default-build
81+regression and NOT a clear bug; it's a heuristic-threshold calibration question confined to the zen-only JPEG
82+path. `gq >= 85.0` would flip High → 4:4:4 there, but whether that better approximates evalchroma is
83+content-dependent — needs corpus measurement, not a blind 1-char change.
84+ 
85+### MEDIUM — single-frame PNG/WebP/AVIF/JXL decode ignores `max_threads`
86+*Source-verified 2026-06-24.* Only JPEG takes the buffered `run_pooled` path; PNG/WebP/AVIF/JXL single-frame
87+decode runs `job.push_decode(...)` on the ambient global rayon pool (`zen_decoder.rs:~932`), so
88+`ExecutionSecurity.max_threads` is silently violated for exactly the most-parallel decoders (rav1d/jxl-rs).
89+**Fix:** run the single-frame `push_decode` inside `install_pooled(&self.thread_pool, …)` (mind the `&mut`
90+bitmap-window sink — the closure must own/move it).
91+ 
92+### LOW / interim (2026-06-24 audit)
93+- `v1/estimate` ignores `data.format` (`v1.rs:301` `let _ = &data.format;`) — encode side always 0, so the
94+ returned `EncodeEstimate` is decode-only. Part of the #728 seam (codecs return `ResourceEstimate::unknown()`).
95+- `check_estimates` `peak_max = …unwrap_or(peak_avg)` collapses the conservative gate onto the avg when a
96+ codec sets `est` but not `max` (`ResourceEstimate::new` does exactly that). Apply a conservatism factor or
97+ have codecs always set `max`.
98+- `LIBJPEG_TURBO_Q_TO_SSIM2` (`auto.rs:649`) duplicates the `QUALITY_HINTS.ssim2` column verbatim — second
99+ source of truth that will drift; generate one from the other.
100+- Per-decode eager rayon pool (`zen_decoder.rs:239`) built even for single-frame decodes that never use it;
101+ one-shot encode rebuilds a pool per call (`zen_encoder.rs:672`). Build lazily / reuse `self.thread_pool`.
102+- Zero tests for the budgeting/estimate math (`check_estimates`/`byte_ceiling`/`v1/estimate`) and zero for
103+ mid-flight cancellation. The `>=` reject boundary and the cap interaction are untested.
104+ 
105+## Audit Notes (2026-06-22, "since v2.3.1-rc01" review)
106+ 
107+- **CHANGELOG.md:13 is inaccurate**: claims `ExecutionSecurity` "gains … process timeout, and
108+ cooperative cancellation handles." The actual struct (`imageflow_types/src/lib.rs:1127-1144`) has
109+ ONLY size/byte/pixel limits — no timeout field, no cancellation handle. `JobOptions`
110+ (`lib.rs:1476`) is an empty `#[non_exhaustive]` placeholder. No process timeout exists anywhere.
111+- **CHANGELOG AVIF/HEIC drift**: AVIF decoder+encoder and BMP/PNM (`zenavif`/`zenbitmaps`) already
112+ SHIPPED in `1bb00db5` but are listed under "QUEUED BREAKING CHANGES" as if pending; **HEIC is
113+ genuinely absent** (no `heic` refs in `src/`). `ZenJxlDecoder` enum variant exists with no `zenjxl`
114+ dep (scaffolding). `f06b478b` (moxcms widen) is uncited; `8e6f2483`→`bd545d11` IDCT churn is invisible.
115+- **Privacy metadata = stripped by construction** (clean result): no encoder writes EXIF/XMP/IPTC/GPS;
116+ EXIF orientation is applied-to-pixels-then-dropped; no preserve-metadata option exists. Source ICC is
117+ read for the color transform but never re-embedded — output is plain sRGB-by-convention (only the C
118+ libpng path writes an sRGB marker, `codec_png_wrapper.c:420`).
119+- **Quality units**: only a codec-agnostic 0–100 scalar (`QualityProfile`, `lib.rs:644`) → static
120+ per-codec tables (`auto.rs:557-716 QUALITY_HINTS`). No metric-target unit (zensim/ssim2/butteraugli);
121+ `jxl.distance` is the only metric-flavored knob (JXL-only passthrough). The `ssim2` column in
122+ `QUALITY_HINTS` is internal DPR-math only, not a request unit. `zensim` is a **dev-dependency only**.
123+ `QualityIntent`/`codec_decisions.rs` (the richer design) is on branch `feat/zen-codecs-v3`, NOT HEAD,
124+ and still collapses to one `generic_quality` float — would need structural change for metric targeting.
125+ 
126+## Delayed TODOs
127+ 
128+- **ssim2↔quality table calibration — JPEG dials DONE, remaining axes still guessed.**
129+ - **DONE (2026-06-26):** `LIBJPEG_TURBO_Q_TO_SSIM2` in `imageflow_core/src/codecs/auto.rs` is now
130+ **measured** (24 anchors), and a companion `MOZJPEG_EVALCHROMA_Q_TO_SSIM2` (24 anchors,
131+ `#[allow(dead_code)]` until the JPEG path wires it) was added. Both are the median quality→SSIMULACRA2
132+ curve from an 81,552-cell sweep (codec-corpus 502 images × {64,256,1024,native≤4MP} × q5–q100 × 2
133+ encoders, fast-ssim2). Canonical copy + the `q_to_ssim2`/`ssim2_to_q`/`q_to_bpp` helpers + full
134+ provenance live in `zencodecs::quality_calibration` (zenpipe); raw Parquet at
135+ `/mnt/v/output/jpeg-q-ssim2-cal/2026-06-26/sweep.parquet`; docs + rosetta CSVs in
136+ `imageflow/benchmarks/jpeg-q-ssim2-2026-06-26/`.
137+ - **STILL UNCALIBRATED:** the `ssim2` column of `QUALITY_HINTS` (the DPR/quality-scalar math) has NO
138+ empirical backing. The measured tables cover **JPEG dials only** — WebP/AVIF/JXL quality→ssim2 were
139+ NOT swept, so the generic-quality target for those codecs is still a guess.
140+ - **FIXED (2026-07-19) — the double-mapping correctness bug** (see the Known Bugs entry above):
141+ auto.rs now feeds `with_generic_quality` the profile's *libjpeg-turbo 0–100 dial*
142+ (`zencodec_generic_quality`); the ssim2-unit `generic_quality_ssim2` is reserved for the JPEG
143+ `ApproxSsim2` path. Remaining follow-up: sweep WebP/AVIF/JXL to calibrate their own quality→ssim2
144+ curves the way JPEG's dials now are (the codecs' internal libjpeg-q→native tables are CID22-512
145+ medians; imageflow-side per-codec ssim2 columns in `QUALITY_HINTS` are still guesses, per the
146+ bullet above).
147+ 
148+- **Issue #728 zencodec passthrough (currently interim heuristic).** The `target=fast|optimal` +
149+ balance directive and `is_optimal`/optimality-headroom annotations are implemented in imageflow with
150+ an INTERIM local cost/RD heuristic (`auto.rs`), because zencodec 0.1.19 exposes no encode
151+ resource-estimate, no candidate-`ImageFormat` selector, and no optimality API. zencodec 0.1.24 adds
152+ `EncoderConfig::estimate_encode_resources` plumbing but every codec returns `ResourceEstimate::unknown()`.
153+ Full passthrough needs: (1) imageflow on zencodec ≥ the release that ships real
154+ `estimate_encode_resources` impls in zenavif/zenjpeg/zenwebp/zenjxl, and (2) NEW zencodec APIs (a
155+ candidate-format selector + optimality/would-not-improve determination) that exist in no version yet.
156+ Replace the interim seams (marked `// TODO(#728): zencodec passthrough`) when those land.
157+ 
158+- **Licensing/caching module** (`imageflow_helpers/src/unused/`): ~2300 lines of draft licensing, caching, and polling code. Currently unreferenced (no `mod` declaration). Needs review, modernization, and wiring into the build when ready to complete.
13159  
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack