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 |