feat(galaxies): physical surface-brightness rendering + radial colour gradient (look spike)#502
Merged
Merged
Conversation
Galaxies rendered as flat LDR disks that never crossed the bloom threshold. Replace the apparent-magnitude clamp with a physically grounded surface-brightness amplitude so intrinsically bright/compact galaxies emit into HDR and bloom, while diffuse ones stay dim. - Bake per-galaxy sbAmp = 10^(-0.4*(M_abs - meanAbsMag_perCatalog)) / (diameterKpc/30)^2 into a new interleaved slot (13->14 slots). - Vertex intensity = sbAmp * sbScale * sbBoost * fluxFalloff, where fluxFalloff = pow(resolvedFrac, falloffStrength) gated by the depth toggle recovers inverse-square on approach while resolved galaxies hold constant surface brightness. - Wire three live calibration knobs (sbScale, sbMax, falloffStrength) through the store to the points Uniforms (176->192 bytes) with sliders in Settings > Galaxies > Advanced. - Repurpose the now-dead per-source intensityFloor field into an sbBoost multiplier; lift Milliquas 3x. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
skymap | a06954c | Commit Preview URL Branch Preview URL |
Jul 24 2026, 06:41 PM |
Stage 2 of the galaxy-look spike. The close-approach procedural disk handed off from a physically-lit point sprite to a flat-brightness disk (peak ~1.0, never bloomed). Now both passes derive brightness from the same physical surface-brightness model, so bright galaxies bloom in the disk view too and the point->disk crossfade holds constant brightness. - Extract the SB formula into shared pure helpers galaxySbAmp + galaxyMeanAbsMag; the bake and the disk planner both use them so the amplitude can't drift between passes. - Store the per-catalog SB zero-point on GalaxyCatalog.meanAbsMag (optional; populated at decode/synthetic/empty/transfer, recomputed on decode so no .bin format bump). The bake reads it; the disk planner reads catalog.meanAbsMag directly, so no store map or frame-input threading of the zero-point. - Disk planner computes each instance's effective amplitude min(sbAmp, sbMax) * sbScale * sbBoost * brightness from the live settings each frame and packs it into the instance's free extras.w slot; the fragment multiplies its profile by it. No disk-uniform growth, so the Advanced sliders stay live. - Tighten the core profile: bulge sigma 0.4->0.28, weights 0.6/0.4 -> 0.7/0.3, so the SB amplitude reads as a concentrated bloomy nucleus over a softer disk. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The disk read as a flat blob: bulge sigma 0.28 with 0.7/0.3 weights left the mid-disk floor only ~4x below the centre, so core and halo bloomed together. Tighten the bulge to sigma 0.18 and shift the weights to 0.85/0.15. The weights still sum to 1.0, so the CENTRE peak stays at 1.0 x sbAmp — the value that already matches the companion point sprite, so this cannot reintroduce a brightness pop at the crossfade. Contrast comes entirely from draining the halo and tightening the bulge, dropping the mid-disk floor to ~14x below the peak: the nucleus clears the bloom threshold while the halo stays under it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Famous galaxies rendered blown out on close approach. The cause is a systematic in the surface-brightness model, not missing photometry — 77 of the 80 seed rows carry a real magB. galaxySbAmp divides a catalog-RELATIVE luminosity (normalised against the catalog's own meanAbsMag) by an ABSOLUTE size reference (fixed 30 kpc). Famous has a median diameter of 25.4 kpc, so the size term alone inflates every row by 1/0.847^2 = 1.39x; with the log-space-mean- then-exponentiate skew the measured median raw lands at 2.14 against a nominal 1.0, tailing to 11.7 (NGC 4449). Every row then clears the 2.0 bloom threshold, so the whole catalog blooms at once. The sbMax ceiling never engages — 0 of 80 rows reach it. 0.45 ~= 1/2.14 re-centres the catalog's median on the amplitude a typical survey galaxy gets while preserving the internal spread. This is a per-source trim; normalising the size term per-catalog would fix the relative-vs-absolute mismatch generally, at the cost of re-tuning every other catalog. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…esign The look spike grounded galaxy brightness in a physical surface-brightness amplitude, but the normalisation is not actually physical: galaxySbAmp divides a catalog-RELATIVE luminosity (against that catalog's own meanAbsMag) by an ABSOLUTE 30 kpc size reference. Records the three defects with derivations so the reasoning isn't lost: meanAbsMag conflating a band zero-point with real luminosity differences; Famous's sbBoost 0.45 being fitted to a symptom rather than photometry; and GLADE's diameter being Tully(1988)-derived from its own B magnitude, making SB ∝ L^-0.245 — a re-parameterisation of the magnitude carrying no independent surface-brightness information. Also notes the honest cheap option: keep the tuning, drop the "physically grounded" claim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Circinus and the Milliquas copy of Centaurus A rendered as blown-out
white blobs. The cause is upstream data, not the brightness model.
Milliquas marks an absent magnitude with a literal `0`, not a blank. The
real Circinus row reads `Rmag="10.93" Bmag=" 0 "` — R measured, B absent
— but the parser only guarded blanks, so parseFloat('0') came through as
a real magnitude. 2169 rows carried magG=0.
Zero is catastrophic rather than merely wrong: at Circinus' 4.28 Mpc an
m=0 back-solves to M=-28.2, which galaxySbAmp reads as 243x a typical
galaxy's luminosity. Clamped at sbMax it still renders at sbAmp 450
against a catalog median of 15 — 30x too bright.
Treating 0 as missing is safe for this catalog specifically: the
brightest known AGN (3C 273) sits at ~12.9, so no row has a legitimate
magnitude near zero. NaN is the honest sentinel and downstream consumers
already handle it.
Regression test appends the real Circinus row to the fixture verbatim.
Takes effect on the next Milliquas bin rebuild.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Milliquas records were added straight into the per-source bucket after dropFamousMatches had already run on the crossMatch output — which Milliquas is deliberately absent from — so they silently skipped the famous dedup entirely. A famous galaxy with an active nucleus therefore rendered twice: once as its curated entry, once as a Milliquas point on top. Centaurus A was the visible case. The existing "Milliquas bypasses crossMatch on purpose" rationale is sound but covers only crossMatch (an AGN core and its host are different objects, and crossMatch dedups on RA/Dec/redshift). Skipping the famous dedup was an unexamined side effect of where the injection happened. Measured cost: 20 of ~943k Milliquas rows sit within 30" of a famous-seed position, and only ONE is genuinely a different object — a ~1 Gpc background quasar behind the Antennae. The other 19 are the host's own nucleus, scattered in distance only by Milliquas' coarse 3-decimal redshift (z=0.001 quantises to 4.28 Mpc, 0.002 to 8.56). Accepted trade; a redshift-agreement test would save the quasar if it ever matters. Drop count is logged rather than silent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…asses Galaxies were flat-tinted: one ramp lookup per galaxy, the same hue from core to rim. Real galaxies are not — the bulge is old red stars, the arms young blue OB stars, a g-r difference of roughly 0.4. The gradient is expressed in COLOUR-INDEX space and re-uses the existing shared 'ramp', rather than as an ad-hoc RGB tint. That distinction is what makes it safe on a catalog: a red elliptical stays red and merely gains internal structure, instead of being force-tinted blue at its rim. The split is symmetric, so a galaxy's integrated hue stays near its catalogued colour index, and 'ramp' saturating at the extremes makes the gradient self-limiting for already-extreme colours. An earlier attempt at warm-bulge/cool-disk tinting was reverted because it lived only in the procedural-disk pass and so diverged visibly from the points pass across the 8-14 px crossfade. Both passes now read the same 'rampCore'/'rampRim' pair from 'lib/colorIndex.wesl', so that divergence is structurally impossible rather than merely avoided. The two passes spell the reconstruction differently for cost reasons, not semantic ones. The procedural-disk pass has few instances and evaluates both ramps per fragment. The points pass covers most of the screen at the large tiers and is fragment/blend-bound, so it hoists both ramp lookups to the vertex stage and forwards the rim-minus-core delta as a flat varying; the fragment reconstructs with one fused multiply-add. Both are exact lerps between the same two endpoints, and the endpoints are per-instance constants, so the passes agree value-for-value. Known approximation: the point sprite's billboard is padded relative to the galaxy's true extent while the disk impostor's r = 1 is the disk edge, so the gradient is radially compressed on the point relative to the disk. The passes cross-fade rather than swap, so this reads as the gradient gaining definition on approach. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng budget The radial gradient forwarded a precomputed rim-minus-core delta from the points vertex stage, which needed a fifth @location on VSOut. That cost +1.5 ms on the point-sprites slot (~21% at the full-survey scenario, 60 frames, alternating A/B) against a per-fragment saving of one sqrt and one fused multiply-add. Varying count is a real budget here: each @location is a per-fragment parameter fetch from tile memory paid for every covered pixel, and this pass is fragment/blend-bound with billboards covering most of the screen at the large tiers. Two changes remove the slot. 'ramp' is piecewise linear in its saturated parameter, so mixing two lookups that land on the same segment equals one lookup at the mixed argument: mix(ramp(t+d), ramp(t-d), r) == ramp(t + d - 2*d*r). The rampCore/rampRim pair collapses to a single 'rampRadial(t, r)'. No pass needs to forward an endpoint delta any more, and the procedural-disk pass drops from two ramp evaluations per fragment to one. VSOut then repacks rather than extends. A WGSL @location is a 4-component slot, so a lone vec2 wastes two components. Grouping by what actually consumes the values together yields 'ellipse' (paCos, paSin, safeAB — exactly the three inputs to the elliptical-mask transform) and 'instanceTint' (the per-instance modulator plus the ramp input it scales). VSOut is back to 4 locations, its count before the gradient existed. The ramp evaluation necessarily moves to the fragment stage: its argument depends on the fragment's radius, so it could not live in the vertex stage under any spelling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t in measurements
Two fixes to lib/colorIndex.wesl.
## The ramp had a hard seam at t = 1
Both halves shared 's = saturate(t * 0.5)', so the blue-to-white half only
reached HALFWAY to white at t = 1 while the white-to-red half STARTED
halfway to red. The function jumped by (+0.30, -0.05, -0.35) there and
never passed through its own white anchor, despite the header calling
white a shared join point and the transition continuous. Each half now
spans its own unit interval, 'saturate(t)' and 'saturate(t - 1)', so both
meet at white exactly.
This stayed invisible for as long as each galaxy sampled the ramp at a
single point: the step only separated one galaxy from another. The radial
colour gradient sweeps one galaxy's argument across a range as the
fragment radius varies, which turned the step into a hard-edged ring
inside a single galaxy. A ramp sampled once per galaxy can hide a
discontinuity; one swept per fragment cannot.
Independent confirmation this was a bug and not a deliberate palette:
UNKNOWN_COLOUR_RAMP_POSITION is documented as "a deliberately pale,
neutral position", and 1.05 only renders pale under a continuous ramp. It
rendered bright orange before.
Expect a global recolouring. The endpoints are unchanged, but the
mid-range becomes markedly whiter, and the large unknown-colour
population at 1.05 goes from orange to pale cream.
## The spread was a guess; now it is derived
DISK_TINT_SPREAD drops from 0.4 to 0.2. The old value rested on an
unsourced claim about bulge and arm colours. Three independent
measurements of the centre-to-edge gradient, all bluer-outward:
- ~20,000 face-on SDSS-DR4 spirals: -0.006 mag/kpc in g-r, so about
0.09 mag over a ~15 kpc disk radius.
- SDSS early-types: d(g-r)/d log r of -0.11 to -0.16 mag/dex, about
0.11 to 0.16 mag over the ~1 dex the visible disk spans.
- Bulge-versus-disc component colours: about 0.23 mag in g-r.
Taking 0.15 mag. The constant is in RAMP-POSITION units, not magnitudes:
colourIndex.ts remaps each source's raw colour onto 0..2, so a magnitude
is worth 2/(rangeMax-rangeMin) ramp units, which is per-source — 2MRS
5.00, DESI 2.86, SDSS and Famous 1.33, Milliquas 1.00, GLADE 0.67. At the
median factor, 0.15 mag gives 0.2.
Known simplification, recorded in the file: one shared ramp-space constant
against per-source conversion factors means the effective physical
gradient differs by catalog, roughly 0.04 mag for 2MRS up to 0.30 mag for
GLADE. Making it exact needs a per-source spread derived from each
colourSpec range and that band's own measured gradient.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Main's #501 replaced the highlightFallback + realOnly boolean pair with a general per-axis provenance system, and added persisted orientationIsFallback / diameterIsFallback arrays to GalaxyCatalog. This branch had independently moved the galaxy colour-ramp evaluation from the points vertex stage to the fragment stage and repacked VSOut. Most of the collision was additive and resolved as keep-both. highlightFallback and realOnly are dropped rather than preserved: main deleted the concept, so the entries, their reducers and their defaults go with it. The one real design clash was the points vertex stage. Main's provenance highlight REPLACES the ramp colour with a flat debug colour and boosts brightness, because multiplying a tint into a red galaxy darkens it instead of making it pop. This branch no longer evaluates the ramp in the vertex stage at all, since the radial gradient's argument depends on the fragment's radius. Resolved via the sign of instanceTint.w. pickColourIndex clamps the colour index to [0, 2], so it is never negative, which makes a negative .w a free and unambiguous sentinel for "highlight active: .rgb is already the final colour, skip the ramp". The fragment collapses the ramp to a multiply by white in that case. This is the same sign-bit-as-flag idiom the codebase already uses for axisRatio (orientation fallback) and radiusMpc (diameter fallback). A debug overlay therefore renders flat rather than gradient-modulated, which is correct — its job is to read as unmistakably synthetic. Both branches also grew the same points uniform struct. The byte ranges turned out disjoint: provenance at 112..127 with depthFadeEnabled moved to 156, against galaxySbScale/galaxySbMax/galaxyFalloffStrength at 172..180. Verified the merged WESL struct against packPointUniforms.ts field by field; UNIFORM_BYTES is 192. Main's "total: 176 bytes" layout comment was stale and is corrected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…obust SDSS medium rendered as a couple of dots — most of its galaxies invisible, though picking still resolved them. The galaxies were being rasterised; they were roughly 25x too dim. SDSS marks missing photometry with the numeric sentinel -9999, not NaN and not an empty field, and the CSV parser only rejected isNaN. The sentinel reached magG, where absoluteFromApparent(-9999, d) returns about -10040 — finite, so the zero-point's Number.isFinite guard accepted it. 55 such rows were enough. They dragged the per-catalog zero-point by -3.49 mag in sdss-medium (157,640 rows) and -1.11 mag in sdss-large (498,268 rows) — the same 55 rows over a third as many galaxies, which is why medium vanished while large merely read dim. Since galaxySbAmp normalises every galaxy against that zero-point, sdss-medium's median sbAmp was 0.038 where DEFAULT_GALAXY_SB_SCALE's own docstring states the invariant that a median row sits near 1.0. Measured, 0.05% of sdss-medium rows exceeded intensity 0.5 against GLADE medium's 43%. The rows survived every filter by design accident: subsampleByAbsMag ranks ascending by absolute magnitude, so -10040 sorted as the 55 BRIGHTEST galaxies and was guaranteed into every tier. Two complementary fixes rather than one. isPlausibleMagnitude rejects anything outside -30 < mag < 40 at the parser. It is an inclusive-range test, not an equality check against known sentinels: catalogs use several (-9999, -999, 99.99) and enumerating them means editing the predicate every time a source is added. Applied to the SDSS, GLADE, 2MRS, Hipparcos and Milliquas parsers. It also subsumes 2MRS's exact `=== 99.999` compare, which a truncated 99.99 would have slipped past. Milliquas keeps its own literal-0 rule composed on top, since 0 is a plausible magnitude in general and only Milliquas means "absent" by it. The DESI, famous-seed and HyperLEDA parsers already guard correctly and are untouched. galaxyMeanAbsMag becomes galaxyMedianAbsMag and takes a median. A mean can be moved arbitrarily far by a single wild value; a median is robust to any future contamination class without this file needing to know which sentinels exist — that knowledge belongs in the parser. On real data the two agree: sdss-medium's median is -20.82 against a sentinel-free mean of -20.880. The field holding it is renamed to medianAbsMag to keep the concept from being half-renamed. The fixes are not redundant. The parser repairs the data and the tier ranking; the median makes the statistic unwreckable by anything that slips through. Only the median takes effect on already-built bins — the parser fix needs a build-tiers run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
rulkens
marked this pull request as ready for review
July 24, 2026 18:31
Per-source colour-gradient spread: DISK_TINT_SPREAD is one shared constant in ramp-position units, but ramp units per magnitude are fixed per source by its own colourSpec range, so one knob renders a 7.5x spread in the physical core-to-rim gradient (~0.04 mag for 2MRS to ~0.30 mag for GLADE). Famous-seed redshift-distance fallback: M90 bakes at 1.47 Mpc because HyperLEDA's mod0 misses its error gate by 0.05 mag and the chain falls to v3k/70, invalid for a Virgo infall member whose CMB-frame velocity is mostly peculiar motion. The wrong distance drags diameterKpc with it, so the galaxy mispositions, undersizes, and mis-brightens at once. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Galaxy look spike: physical surface brightness + radial colour gradient
Live-tuning spike to make the current point / procedural-disk galaxies pass visually before committing to replace the procedural disks with the full galaxy renderer. The motivating problem: galaxy points were flat LDR and never crossed the HDR bloom threshold (2.0), so they read as dull dots that never bloom.
Run as a live-tuning spike rather than spec + plan, iterating on the dev server. All three stages have had a visual pass.
Stage 1: physical surface brightness in the points pass
Galaxy point brightness is driven by physical surface brightness instead of a flat constant.
sbAmp = 10^(-0.4·(M_abs − perCatalogZeroPoint)) / (diameterKpc/30)²written into interleaved slot 13 (SLOTS_PER_POINT13 → 14). No.binformat change:magG+diameterKpcalready live in the cloud, so this is bake-time only.min(sbAmp, u.galaxySbMax) · u.galaxySbScale · source.sbBoost · fluxFalloff, wherefluxFalloff = select(1, pow(resolvedFrac, u.galaxyFalloffStrength), depthFadeEnabled)andresolvedFrac = apparentPxRadius / pointSizePx. Constant surface brightness while resolved, inverse-square dimming once floored, so galaxies grow into view and bloom on approach. The olddepthFade = 1/(1+(d/half)²)is removed and the toggle now gates the new falloff.Three live knobs in Settings → Galaxies → Advanced (points
Uniformsgrown 176 → 192 bytes):sbScale(5.0),sbMaxbloom ceiling (30),falloffStrength(0.7). Per-sourcesbBoostrepurposes the deadintensityFloorfield, 1.0 everywhere except Milliquas at 3.0 and Famous at 0.45.Stage 2: the same physics in the procedural-disk pass
Both passes now bloom, and the point → disk crossfade holds constant brightness through the handoff.
The formula lives in shared pure helpers (
src/utils/galaxy/galaxySbAmp.ts,galaxyMedianAbsMag.ts) called by both the bake and the disk planner, so the two cannot drift. The per-catalog zero-point rides onGalaxyCatalog.medianAbsMagas an optional field recomputed at decode, so there is no.binformat bump. The disk instance's effective amplitude is computed each frame from the live sliders and packed into the existing freeextras.wslot, so no disk uniform grows and the sliders stay live. Core profile tightened:BULGE_SIGMA0.4 → 0.28, weights 0.6/0.4 → 0.7/0.3.Stage 3: warm-core / cool-rim radial colour gradient
Expressed in colour-index space re-using the shared
ramp, not as an ad-hoc RGB tint. That choice is load-bearing: a red elliptical stays red and merely gains structure, andrampsaturating makes the gradient self-limiting at the extremes.rampRadial(t, r) = ramp(t + DISK_TINT_SPREAD · (0.5 − r)), called by both passes from the same helper, so the divergence that got an earlier warm-bulge/cool-disk tint reverted is structurally impossible.Two things came out of the first cut:
@location, which measured +1.49 ms on the point-sprites slot (about 21%) against a 0.07 ms noise floor. Collapsing the two ramp lookups into onerampRadialand repackingVSOutintoinstanceTint: vec4(rgb = tint × intensity, w = colour index) andellipse: vec3(paCos, paSin, safeAB) returns it to the four locations it had before the gradient existed. Re-measured on an idle machine at +0.10 ms, at the noise floor, which confirms the varying rather than the arithmetic was the cost.ramphad both halves sharings = saturate(t · 0.5), so the blue → white half only reached halfway to white at t = 1 while the white → red half started halfway to red. The function jumped by (+0.30, −0.05, −0.35) at t = 1 and never passed through its own white anchor. This stayed invisible while each galaxy sampled the ramp at exactly one point; the radial gradient sweeps one galaxy's argument across a range and turned the step into a hard ring inside a single galaxy. Independent confirmation it was a bug:UNKNOWN_COLOUR_RAMP_POSITION = 1.05is documented as a deliberately pale, neutral position, which it only is under a continuous ramp.This recolours the whole galaxy field. Endpoints are unchanged (t = 0 blue, t = 2 red), but the mid-range is markedly whiter and the large unknown-colour population at 1.05 goes from orange to pale cream.
DISK_TINT_SPREAD = 0.2is grounded in a measured centre-to-edge gradient of about 0.15 mag from three sources (SDSS-DR4 spirals, SDSS early-type galaxies, and a bulge-vs-disc component offset), converted at the median per-source ramp factor of 1.33.Catalog fixes found along the way
These are independent of the rendering work but were surfaced by it.
Sentinel magnitudes. Catalogs mark absent photometry with numeric sentinels that pass
Number.isFinite: SDSS-9999, 2MRS99.999, Milliquas a literal0. New shared guardtools/utils/math/isPlausibleMagnitude.ts(-30 < mag < 40), an inclusive-range test rather than an enumeration of known sentinels so a new source with a new sentinel needs no edit. Applied in the SDSS, GLADE, 2MRS, Hipparcos and Milliquas parsers.This was the cause of SDSS medium rendering as a handful of dots while picking still resolved every galaxy. 55 rows with
M = -10040dragged the per-catalog zero-point by −3.49 mag at medium against −1.11 at large, the same 55 rows over a third as many galaxies. They survived every filter becausesubsampleByAbsMagranks ascending, so they sorted as the 55 brightest galaxies and were guaranteed into every tier.Median rather than mean zero-point.
galaxyMeanAbsMag→galaxyMedianAbsMag(and the field it populates) as the complementary fix. A mean is movable by one wild value; a median is robust to any future contamination without knowing which sentinels exist. On real data they agree (−20.82 against a clean mean of −20.880).Milliquas famous-seed dedup.
buildAllBinsrandropFamousMatcheson the cross-matched sources and then injected Milliquas afterwards, so Milliquas rows never saw the famous dedup and Cen A rendered twice. The documented "Milliquas bypasses crossMatch on purpose" rationale covers the crossMatch bypass (AGN core vs host is different science) but not the famous dedup, which was an unexamined side effect. Cost measured at 20 of 943,460 rows, with the drop count logged to stderr rather than culled silently.Deploying this
.binfiles reach production through R2, not git, sonpm run sync-r2-securefrom the main checkout is required after merge or production keeps serving the poisoned SDSS rows and the duplicated Cen A. The bins have already been rebuilt locally.Gate
npm run typecheckclean;npm testgreen at 866 files / 5018 tests.Follow-ups (filed in the backlog, not in this PR)
galaxySbAmpdivides a catalog-relative luminosity by an absolute 30 kpc size reference, Famous is fudged withsbBoost 0.45, and GLADE's diameter is Tully-derived from its own B magnitude so it carries no independent surface-brightness information.DISK_TINT_SPREADis one shared constant in ramp-position units, but ramp units per magnitude are per source, so the effective physical gradient runs about 0.04 mag for 2MRS to 0.30 mag for GLADE.v3k / 70fallback, invalid for a Virgo member whose peculiar velocity dominates its Hubble flow.🤖 Generated with Claude Code