April 20: Auto-Stretch Lands, Production Config Defaults, and the Big Dep Drop
The compositing quality story hit its capstone: smart auto-stretch with histogram analysis and per-channel controls (#688/#1206) is finally merged. Same day shipped the production Docker service-name defaults, hardened mosaic generation against enumeration, migrated to photutils 3.0, and cleared a 17-PR Dependabot backlog in one sitting.
Developer Journal
Smart auto-stretch with histogram analysis (#1206, closes #688)
Issue #688 had been open for months — the full-stack auto-stretch feature. Today it shipped. The changes span every layer:
Processing engine — new auto-stretch module that analyzes each channel's histogram and picks black/white points that preserve foreground detail without clipping structure. Uses a percentile-based initial estimate (0.5% / 99.5%) refined by detecting the noise floor (MAD-based) and the bright tail rolloff. Returns both the per-channel recommendations and a combined "auto all" result that ensures the channels are visually consistent (no single channel dominating due to overly aggressive stretch).
Backend API — exposed auto-stretch as a dry-run endpoint that returns the recommended points without running the composite. Frontend calls it to populate the UI, user can accept or tweak, then submit the full composite with the chosen values.
Frontend — added histogram visualizations per channel, with live markers for current black/white points. "Auto" button per channel calls the endpoint for that channel, updates the markers, redraws the histogram. "Auto All" runs all channels and the combined pass. Saved as a named preset so it can be reused across composites.
Preset system also got a refresh to store histograms alongside the stretch parameters, so when you load a preset you get the context of what image it was tuned on. The UX felt clumsy the first few passes — "what do these numbers mean?" — and adding the histogram visualization was the thing that made it click.
Total: ~2k lines across frontend, backend, and processing engine. Multi-consumer review was essential (per the rule from #1129 — this PR explicitly touches both the wizard and the guided create flow).
Mosaic generation hardening (#1340)
Followed the April 14 data-enumeration pattern — GenerateAndSaveMosaic was returning different errors for "target not found" vs "target not authorized." Normalized to 404 for both. Also restored the auth-denial logging that a previous refactor had accidentally pulled out — the log was going to Debug level in production and getting discarded, so attempted unauthorized access was invisible to operators. Back to Warning level with full context (user, resource, reason).
Set Docker service-name defaults in appsettings.Production.json (#1347)
Production was failing to boot because the service names in appsettings.Production.json were pointing to localhost:<port> — correct for local dev, wrong for compose which resolves by container name. Set the production defaults to backend, processing-engine, mongo — the docker-compose service names — so a clean prod boot works without env overrides. Env vars still take precedence when set.
Migrate to photutils 3.0.0 (#1342)
photutils 3.0 is a major version bump with breaking API changes in the detection functions. Updated the call sites that used the changed signatures (DAOStarFinder and IRAFStarFinder now take their parameters as a config object instead of positional args), and the segmentation output format change (from structured array to SegmentationImage wrapper).
No functional changes — all existing tests pass with the new API. The upgrade was mostly mechanical once the API diff was mapped.
Remove duplicate scope from Dependabot PR titles (#1341)
Dependabot was emitting titles like chore(deps)(deps): bump ... — the double (deps) scope was coming from the updated config in #1135 which added a scope while Dependabot already had one configured. Cleaned up the config so titles are singular chore(deps): bump .... Purely cosmetic, but the old format looked like a bug to reviewers skimming the PR list.
The big Dependabot drop
16 more dep bumps in this session — TypeScript ESLint group, rollup, fastapi, ruff, eslint, typescript (frontend), vite, jsdom, react-router-dom, pydantic, python-multipart, Microsoft.AspNetCore.OpenApi, Microsoft.AspNetCore.Authentication.JwtBearer, Microsoft.CodeAnalysis.NetAnalyzers, Microsoft.Extensions.Http.Resilience, AWSSDK.S3, actions/upload-pages-artifact. All individually boring, all together a productive wipe of the queue.
With #1135's grouping and #1341's prefix cleanup in place, this is the first Dependabot drop where the queue cleared to empty and every PR had a consistent title and conforming body. Nice to see the process improvements paying off.
Side-side note: auto mode and other tools
Auto Mode is finally available in Claude Code (and on desktop) — still room for issues compared to bypass permissions, but far safer. Spun up a tiny throwaway side-project over the weekend (ChillPill) to see what a few turns of autonomous execution feels like on a POC. Mac Fan Control wants $15 for a pro version to expose RPM controls and Claude happily replicated the core of what I wanted in a couple of turns — not a replacement for tools with polished UX, but impressive for scratch-your-own-itch tooling.
What shipped
| PR | Title |
|---|---|
| #1347 | fix: set Docker service-name defaults in appsettings.Production.json |
| #1346 | chore(deps): bump react-router-dom in /frontend/jwst-frontend |
| #1345 | chore(deps-dev): bump eslint in /frontend/jwst-frontend |
| #1344 | chore(deps): bump pydantic from 2.13.0 to 2.13.3 in /processing-engine |
| #1343 | chore(deps-dev): bump jsdom in /frontend/jwst-frontend |
| #1342 | fix: migrate to photutils 3.0.0 |
| #1341 | chore: remove duplicate scope from Dependabot PR titles |
| #1340 | fix: harden GenerateAndSaveMosaic against enumeration and restore auth-denial logging |
| #1315 | chore(deps): Bump Microsoft.Extensions.Http.Resilience from 10.4.0 to 10.5.0 |
| #1314 | chore(deps): Bump Microsoft.CodeAnalysis.NetAnalyzers from 10.0.201 to 10.0.202 |
| #1313 | chore(deps): Bump Microsoft.AspNetCore.OpenApi from 10.0.5 to 10.0.6 |
| #1312 | chore(deps): Bump Microsoft.AspNetCore.Authentication.JwtBearer from 10.0.5 to 10.0.6 |
| #1309 | chore(deps): Bump AWSSDK.S3 from 4.0.21.1 to 4.0.21.2 |
| #1308 | chore(deps-dev)(deps-dev): bump vite in /frontend/jwst-frontend |
| #1306 | chore(deps-dev)(deps-dev): bump typescript in /frontend/jwst-frontend |
| #1305 | chore(deps)(deps): bump ruff in /processing-engine |
| #1304 | chore(deps-dev)(deps-dev): bump the typescript-eslint group |
| #1303 | chore(deps)(deps): bump fastapi in /processing-engine |
| #1302 | chore(deps)(deps): bump actions/upload-pages-artifact from 4 to 5 |
| #1206 | feat: add smart auto-stretch with histogram analysis and per-channel controls (#688) |
| #1166 | chore(deps)(deps): bump python-multipart in /processing-engine |