Skip to content

March 24: Feathering the Wrong Thing

Three compositing bugs in a row — turned out per-channel feathering was running on single-instrument composites where it makes no sense, the memory budget for NIRCAM was miscalculated, and the multi-instrument feather needed a union mask instead of per-channel intersections.

Developer Journal

Feathering a single instrument does nothing

Caught a dumb bug: per-channel feathering was firing on composites with only one instrument. Since all channels of a single instrument share the same footprint, the feather masks come out identical and the feather does literally nothing except burn CPU and subtly dim the edges.

The fix disables per-channel feathering when the composite has only one contributing instrument — the signal-fade path is only useful when there's a boundary between different coverage regions. Added the guard at the top of the feathering stage in compositeService.py so it short-circuits before computing any masks.

Memory budget was wrong for NIRCAM

The composite memory budget calculation assumed a typical 6-channel NIRCAM stack, but the downscale helper was being called with the full pre-downscale dimensions. For a 12k × 10k input, the budget check passed (post-downscale size was fine) but the intermediate buffers allocated at the pre-downscale size would OOM.

Fixed by computing the budget from the target working resolution rather than the source resolution. Also added a clearer error when the budget check fails — previously it just OOM'd, now it raises with the computed size and the configured limit so you can actually tune it.

Union mask for per-channel feathering

When per-channel feathering is legitimately needed (multi-instrument), each channel should feather against the union of all instrument coverages — not its own coverage intersected with the feather width. Otherwise channels at the edge of a less-covered instrument get feathered to nothing while the overlapping instrument stays fully opaque, creating a dim ghost band.

Switched the mask computation to np.logical_or.reduce across the per-instrument coverage masks before the per-channel distance transform runs. The transition now fades consistently regardless of which instrument is dominant at any given pixel.

CEO review skill: auto-file risk issues

Improved the plan review workflow. When CEO review flags a risk item that could be out of scope for the current PR, it now auto-opens a GitHub issue with the tech-debt label instead of requiring a manual handoff. Also allowed multiple checkbox items in the tech-debt section of a review — the previous parser only honored the first one. Small QoL, but it closes a gap where risk items were getting lost between the review and implementation.

What shipped

PR Title
#915 chore: CEO review skill auto-files risk issues + allow multiple tech debt checkboxes
#914 fix: disable per-channel feathering for single-instrument composites
#909 fix: correct composite memory budget to prevent OOM on large NIRCAM data
#907 fix: use union coverage mask for per-channel feathering
#902 Bump Swashbuckle.AspNetCore from 10.1.4 to 10.1.5
#898 chore(deps): Bump @rolldown/binding-linux-x64-musl
#896 chore(deps-dev): Bump @vitest/coverage-v8 in /frontend/jwst-frontend