Goal: elevate the image-processing engine to a production level. A 10-agent
execution-verified audit swept all 521 operators / 11 backend modules plus the
evolution core, CLI, codegen and parity paths. 81 findings were reported (raw
data with repros: docs/audit_findings_2026_08_12.json). Independently, a
mechanical sweep (11,516 invocations) and a large-image scale probe were run.
This document records the disposition of every finding: what was fixed (with a regression test) and what is deferred (with the reason). Nothing is dropped.
tests/ now holds 2255 passing checks (was 0). Run: py -3.11 -m pytest tests/ -q.
| file | what it guards |
|---|---|
test_op_contracts.py |
every op: runs, finite output, deterministic, honours declared sort, region∈[0,1] |
test_known_bugs.py |
named regression tests for each fixed defect below |
test_correctness_anchors.py |
ground-truth math (invert, threshold, gaussian, otsu, sobel, blob-count, psnr…) |
test_pipeline_engine.py |
decode / apply / clip semantics / genome plumbing |
test_evolution_honesty.py |
reproducibility + holdout-never-selected invariant |
Baseline before fixes: 22 failed / 2224 passed → after fixes: 2255 passed / 0 failed. Evolution north-star re-verified (denoise 40 gens: holdout 13.99→21.65 dB, hand=22.72). Coverage unchanged (269/2313).
polar_trans_image / _ext / _inv, polar_trans_region_inv — cv2.warpPolar never wrote the pixels whose source maps outside the image (Cartesian corners outside the polar disc), so those pixels leaked stale, nondeterministic buffer memory (forward branch also unclamped → values up to 2.8e29). Fix: pre-zero the destination buffer + clip. backends_auto.py geom shape.sk_medial — skimage.morphology.medial_axis broke ties with an unseeded RNG. Fix: rng=0. backends.py.np.clip does not strip NaNsk_wavelet, xsp_wiener, xsitk_laplacian_sharpen (+ sk_blur_effect, elliptic_axis feature) returned all-NaN on constant/degenerate images. Fix: a shared finite-safe fallback (backend_safe.sanitize) patches non-finite pixels from the input, wired into every backend guard._safe/_make used to return v (the input) when a library call raised — so a failed region op returned a continuous grayscale image, a failed feature op returned a 2-D array, etc. (xkor_canny, xsk_orb_count, xsk_random_walker, xcv3_brisk_count, segment_image_mser, small-image kornia ops…). Fix: backend_safe.fallback returns a valid, benign value of the declared sort (region→zeros, feature→0.0, contour→empty, image→clip(v)). Every backend now passes its out_sort to _safe.highpass, corner_response, laplace_of_gauss, dots_image, tan_image, sk_shape_index, sk_hessian_det, sk_corner_harris, cv_corner_harris, xkor_harris, xsk2_corner_kr, xsp_morph_laplace, xsk_unwrap_phase, points_harris_binomial) used _norm→[-1,1], and _apply’s [0,1] clip discarded the entire negative half. Fix: a shared signed01(x) maps signed responses to [0,1] with the zero-crossing at 0.5 (0→0.5, ±max→0/1), preserving both signs. backend_safe.signed01 + ops._signed01. Test: tests/test_signed_response.py (range + both-signs-survive). This changes evolution champions (semantic) — done on explicit user selection._find_op first-match-wins on name OR halcon made apply select_shape bind to remove_small (whose halcon alias is select_shape; its fraction-of-image threshold deletes normal-sized objects) → a confidently wrong CLI result (0 objects, no error). This is audit #72 (was rated low; it is not). Fix: prefer an exact op-NAME match, then the canonical halcon op. imgevolve.py. Test: tests/test_cli_resolution.py. Also corrected the image-processing skill’s microscopy recipe (clahe,otsu,fill_up,select_shape → the verified gauss_filter,otsu,fill_up + a separate apply count_obj).reg_close / convex_fill deleted region pixels touching the image border (binary_closing internal erosion used border_value=0). Fix: border_value=1. ops.py.evolve.run raised ValueError for pop ≤ 2 (empty children np.vstack). Fix: clamp mu and guard the stack. evolve.py.apply_genome raised TypeError when a pipeline ended in a contour (np.mean(dict)). Fix: coerce contour→count. ops.py.robust.py (a) read baseline keys hand/trivial.score that baseline.py never writes (they are .holdout) → its honest-disclosure block was dead; (b) selected the best-of-N champion by train but never persisted it (evolve.run had overwritten champion_*.json with each seed, leaving the last seed on disk). Fix: read .holdout; persist the train-selected champion.xkor_canny (low=0.1+0.3a, high=0.3+0.4b) and xwt_firm_denoise allowed low > high. Fix: clamp high ≥ low.These are real, but the fix changes an operator’s semantics and therefore the evolution champions / prior results. Recommend directing these explicitly.
[0,1] clip → NOW FIXED (see the “Signed responses” subsection under FIXED above; signed01). Remaining: highpass_image/bandpass_image (backends_auto freq shapes) were not swept in this pass — verify and apply signed01 there too if signed._ncc_locate is raw cross-correlation, not normalized cross-correlation (only the template is mean-subtracted). True NCC needs local normalization.frei_dir — reported arctan2 argument order swap (direction encoding); convention-dependent, my spot-check was inconclusive.affine_trans_region, zoom_region, projective_trans_region) declared region but the generic geom shape interpolates → fractional values. Recommended: threshold at 0.5 when out_sort=region._gabor not zero-mean (#49), _canny = thresholded Sobel, no NMS/hysteresis (#70), _bilateral borders wrap via np.roll (#69), _clahe drops H%nb trailing rows (#50), xkor_gaussian hard-codes 5×5 for σ up to 3 (#43), xkor_median/xkor_motion_blur zero-pad borders (#44/#67), xsp_dct_denoise DC-relative threshold (#45), sk_adjust_log gain up to 2 (#55), paint_gray operand order (#8), transpose_region semantic + non-square shape change (#47/#64), xcv2_lap_var saturates (#41), convol_image clips negatives (#42), xcv3_gray_hu1/xmh_zernike clamp unbounded invariants (#51).codegen.emit wraps every stage in np.clip(0,1), so generated code crashes/mis-handles feature & contour finals (#16/#21); difftest can’t detect it (#17); exit status ignores the C backend (#58); contour diff only counts contours (#73).accel parity honesty (#9) — the batched sobel/laplace “fast path” diverges from the registry op, but parity()’s interior-only metric can still label it exact. Recommend: make the fast path faithful, or re-label honestly. (These are the beta codegen/GPU paths, not the evolvable registry.)problems._make_barcode/_make_locate/_make_vol_* crash or ignore size for very small sizes (#61/#74/#65); cmd_pipeline lacks the per-stage clip that _apply has (#53); _imread mishandles contour/volume in_sorts (#63); classify uses unanchored substring matching (#54); duplicate registry names across backends (#72); principal_comp on a 1×1 color image (#75); assorted small-image kornia crashes now caught by the sort-aware guard (#76/#77/#78/#59).
Some ops need an algorithm change at XL scale — measured facts:
cv2.warpPolar errors at a 33000-px dimension (OpenCV SHRT_MAX). Polar/warp ops don’t scale; use skimage.warp_polar or downscale.lowpass/highpass allocate O(N²) complex arrays (~16 GB each at 32k²). Use overlap-add tiling or downscale.bilateral 2.3 s, sk_tv 2.2 s, median 1.1 s at 2048² — prohibitive at XL; swap to cv2/GPU (kornia).gaussian, sobel, otsu, clahe, equalize) → process in halo’d tiles.
Recommended next step: a scale.py tiling executor for tile-safe ops + a scale-class annotation per op.