March 9: Spatial Awareness and Bundle Diet
A two-part day. Morning: visual polish, bug fixes, and infrastructure cleanup. Afternoon: the recipe engine learned to look at the sky — cross-instrument composites now only combine observations that actually overlap. Plus a 52% main bundle reduction by lazy-loading page routes.
Developer Journal
Started by fixing the backend test coverage threshold — it was set to 20% but should have been 40%. The real code coverage was 50.5%, but 15,000+ lines of source-generated logging code (LoggerMessage) were inflating the total, pulling the reported number down to 34.1%. Used ExcludeByAttribute for GeneratedCodeAttribute to properly exclude those.
Then went on a visual spree. The constellation pattern background felt dated — wanted something that evokes what JWST actually does. Used Gemini to generate a Webb-style deep field image (subtle, not too busy), cropped the watermark, compressed it, and swapped it in with a 70% dark overlay so text stays readable. Removed the opaque panel background from the My Library page so the deep field shows through. Fixed the floating analysis bar not spanning full screen width — the background and border were on the inner container instead of the fixed-position wrapper.
Found a bug where the View/Spectrum/Table button was permanently disabled on every file card. Root cause: the DataResponse DTO intentionally omits FilePath (it's an internal server path), but the frontend was checking !!item.filePath which was always undefined. The API returns isViewable for exactly this purpose — switched to item.isViewable !== false.
Fixed the E2E CI pipeline — yesterday's MAST proxy extraction (#734) added a new Docker service, but the E2E job never built the mast-proxy image. Added it to both the docker-build cache job and the E2E pre-build step. E2E passed on the fix PR itself, confirming it works.
Last fix: download progress bars were jumping up and down because SignalR updates report fluctuating byte counts between updates. Added monotonic high-water marks using Math.max() at the merge point so progress, bytes, and per-file percentages can only increase. Speed is intentionally left unclamped since it should reflect real-time throughput.
Cleaned up 10 stale git stashes from the past two weeks — all either already merged or trivially reproducible.
Afternoon: Recipe Engine Gets Eyes
The big feature of the day: spatial overlap awareness for the recipe engine. Testing NGC 5134 after #762 added cross-instrument ranking revealed a fundamental problem — the colors were correct (NIRCam cool, MIRI warm), but the composite image was broken. MIRI data appeared in one corner, NIRCam galaxy in another. They were different sky pointings combined into one recipe because the engine grouped by instrument/filter only, with zero spatial awareness.
The fix threads s_ra/s_dec center coordinates through the full pipeline (frontend → .NET → Python), then uses a Vincenty formula for angular separation with union-find single-linkage clustering using known JWST instrument FOV radii. Cross-instrument recipes are now only generated for observation groups that actually overlap on the sky. When no coordinate data is available, it falls back to the old behavior (assume overlap) with a visible warning.
Also set requires_mosaic properly — when the same filter appears at two distinct pointings more than 10 arcseconds apart, the recipe now flags it. 13 new tests cover the spatial math, clustering, chain-linking, and edge cases.
Found a pre-existing bug during review: the C# ObservationDto was silently dropping TObsRelease and DataProductType during deserialization, meaning the Python engine's proprietary-observation and spectral-observation filtering was effectively dead code in production. Fixed in the same PR.
Cache, Coverage, and Bundle
After merging spatial awareness, realized the 48-hour recipe cache would serve stale pre-spatial recipes to returning users. Added a version segment to the cache key so old entries miss automatically.
Tackled the backend coverage numbers — 18,842 generated lines (OpenAPI support, RegexGenerator, Logging) were more than half the codebase and dragging coverage from 50% to 32%. Added a coverlet runsettings file to exclude *.g.cs and generated attributes. Coverage jumped to 60.1% with zero new tests.
The pytest coverage threshold was also misconfigured — --cov-fail-under=60 in pyproject.toml applied even when running a single test file, falsely reporting 12% failure. Moved it to the CI command only.
Last PR: lazy-loaded all 9 page routes in App.tsx. The barrel import (from './pages') was pulling every page into the main chunk. Splitting at the route level dropped the main JS bundle from 188 KB to 90 KB gzip (52% smaller). The plotly chunk (1.48 MB) now only loads when the user navigates to MyLibrary. Total chunks went from 2 to 22.
Also resolved all 6 pre-existing ESLint warnings across the frontend — zero warnings, zero errors for the first time.
What Changed
Features (3)
- #755 replace constellation background with Webb deep field
- #761 add stretch preset selector to discovery result step
- #763 add spatial overlap awareness to recipe engine
Bug Fixes (5)
- #756 View button always disabled due to filePath never in API response
- #757 add missing mast-proxy Docker build to E2E CI pipeline
- #758 prevent download progress bar from jumping backwards
- #764 move pytest coverage threshold to CI only
- #765 invalidate stale recipe cache after spatial overlap update
Performance (1)
- #767 lazy-load all page routes to split main bundle (188 KB → 90 KB gzip)
Infrastructure (2)
- #754 raise backend coverage threshold to 40% and exclude generated code
- #766 add coverlet exclusions for generated .NET backend code
Docs (3)
- #759 enrich blog posts for March 6-9 with Slack journal and images
- #760 add Phase 9 — lightweight community edition plan
- #762 smart recipe ranking and cross-instrument color mapping
14 commits across 14 pull requests. Next: March 10, 2026.